在我解释我想做什么之前,如果你看下面的代码,你会明白它应该做什么吗? (已更新 - 见下文)
Console.WriteLine(
Coalesce.UntilNull(getSomeFoo(), f => f.Value) ?? "default value");
C# 已经有一个 null 合并运算符,它在简单对象上工作得很好,但如果您需要访问该对象的成员则无济于事。
例如
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 的建议删除括号
更新
我非常喜欢 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");
最佳答案
是的,我会理解的。是的,coalesce 是个好名字。是的,如果 C# 有像 Groovy 和其他一些语言那样的 null-safe 解引用运算符会更好:)
更新
C# 6 有这样一个运算符——空条件运算符,?。
例如:
var street = customer?.PrimaryAddress?.Street;
如果您仍需要默认值,请将它与原始的空合并运算符结合使用。例如:
var street = customer?.PrimaryAddress?.Street ?? "(no address given)";
或者,根据问题中的原始代码:
Console.WriteLine(getSomeFoo()?.Value ?? "default");
当然,请注意,只有当最终属性值可用但由于某种原因设置为 null 时可以使用该默认值时,以这种方式提供默认值才有效。
如果 x
的计算结果为 null,则表达式 x?.y
的结果为 null
;否则它是 x.y
的结果。哦,您也可以将它用于条件方法调用:
possiblyNull?.SomeMethod();
关于c# - 扩展 C# 合并运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/665554/