Прежде чем я объясню, что я хочу сделать, если вы посмотрите на следующий код, вы бы поняли, что он должен делать? (обновлено - см. ниже)
Console.WriteLine(
Coalesce.UntilNull(getSomeFoo(), f => f.Value) ?? "default value");
У С# уже есть оператор с косой связью, который хорошо работает на простых объектах, но не помогает, если вам нужно получить доступ к члену этого объекта.
например.
Console.WriteLine(getSomeString()??"default");
работает очень хорошо, но вам это не поможет:
public class Foo
{
public Foo(string value) { Value=value; }
public string Value { get; private set; }
}
// this will obviously fail if null was returned
Console.WriteLine(getSomeFoo().Value??"default");
// this was the intention
Foo foo=getSomeFoo();
Console.WriteLine(foo!=null?foo.Value:"default");
Поскольку это то, что я часто встречаю, я думал об использовании метода расширения (старая версия):
public static class Extension
{
public static TResult Coalesce<T, TResult>(this T obj, Func<T, TResult> func, TResult defaultValue)
{
if (obj!=null) return func(obj);
else return defaultValue;
}
public static TResult Coalesce<T, TResult>(this T obj, Func<T, TResult> func, Func<TResult> defaultFunc)
{
if (obj!=null) return func(obj);
else return defaultFunc();
}
}
Что позволяет мне написать:
Console.WriteLine(getSomeFoo().Coalesce(f => f.Value, "default value"));
Итак, считаете ли вы, что этот код доступен для чтения? Является ли Coalesce хорошим именем?
Изменить 1: удалили скобки, как было предложено Marc
Update
Мне очень понравились предложения от lassevk и отзывы Groo. Поэтому я добавил перегрузки и не реализовал его как метод расширения. Я также решил, что defaultValue избыточен, потому что вы можете просто использовать существующие? для этого.
Это измененный класс:
public static class Coalesce
{
public static TResult UntilNull<T, TResult>(T obj, Func<T, TResult> func) where TResult : class
{
if (obj!=null) return func(obj);
else return null;
}
public static TResult UntilNull<T1, T2, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, TResult> func2) where TResult : class
{
if (obj!=null) return UntilNull(func1(obj), func2);
else return null;
}
public static TResult UntilNull<T1, T2, T3, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, TResult> func3) where TResult : class
{
if (obj!=null) return UntilNull(func1(obj), func2, func3);
else return null;
}
public static TResult UntilNull<T1, T2, T3, T4, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, T4> func3, Func<T4, TResult> func4) where TResult : class
{
if (obj!=null) return UntilNull(func1(obj), func2, func3, func4);
else return null;
}
}
Использование образца:
Console.WriteLine(
Coalesce.UntilNull(getSomeFoo(), f => f.Value) ?? "default value");
Еще один пример:
public class Bar
{
public Bar Child { get; set; }
public Foo Foo { get; set; }
}
Bar bar=new Bar { Child=new Bar { Foo=new Foo("value") } };
// prints "value":
Console.WriteLine(
Coalesce.UntilNull(bar, b => b.Child, b => b.Foo, f => f.Value) ?? "null");
// prints "null":
Console.WriteLine(
Coalesce.UntilNull(bar, b => b.Foo, f => f.Value) ?? "null");