Jud*_*ngo 35 c# monads extension-methods
编辑2015这个问题及其答案已不再适用.它在C#6出现之前被问到,它具有空传播的opertor(?.),它避免了在这个问题和随后的答案中讨论的hacky-workarounds.截至2015年,在C#中,您现在应该使用Form.ActiveForm?.ActiveControl?.Name.
我一直在考虑.NET中的空传播问题,这常常导致丑陋的重复代码,如下所示:
尝试#1常用代码:
string activeControlName = null;
var activeForm = Form.ActiveForm;
if (activeForm != null)
{
var activeControl = activeForm.ActiveControl;
if(activeControl != null)
{
activeControlname = activeControl.Name;
}
}
Run Code Online (Sandbox Code Playgroud)
StackOverflow上有一些关于Maybe <T> monad的讨论,或者使用某种"if not null"扩展方法:
尝试#2,扩展方法:
// Usage:
var activeControlName = Form.ActiveForm
.IfNotNull(form => form.ActiveControl)
.IfNotNull(control => control.Name);
// Definition:
public static TReturn IfNotNull<TReturn, T>(T instance, Func<T, TReturn> getter)
where T : class
{
if (instance != null ) return getter(instance);
return null;
}
Run Code Online (Sandbox Code Playgroud)
我认为这更好,然而,重复的"IfNotNull"和lambdas会有一些语法混乱.我现在正在考虑这个设计:
尝试使用扩展方法#3,可能<T>
// Usage:
var activeControlName = (from window in Form.ActiveForm.Maybe()
from control in window.ActiveControl.Maybe()
select control.Name).FirstOrDefault();
// Definition:
public struct Maybe<T> : IEnumerable<T>
where T : class
{
private readonly T instance;
public Maybe(T instance)
{
this.instance = instance;
}
public T Value
{
get { return instance; }
}
public IEnumerator<T> GetEnumerator()
{
return Enumerable.Repeat(instance, instance == null ? 0 : 1).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
public static class MaybeExtensions
{
public static Maybe<T> Maybe<T>(this T instance)
where T : class
{
return new Maybe<T>(instance);
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:这是滥用扩展方法吗?它比旧的通常的空检查更好吗?
Dan*_*ker 18
有趣的是,有很多人独立选择这个名字IfNotNull,因此在C#中它必须是最明智的名字!:)
我在SO上找到的最早的一个:使用这个(基于扩展方法)速记的可能陷阱
我的(不知道上述内容):C#中的管道转发
另一个最近的例子:如何检查深度lambda表达式中的空值?
IfNotNull扩展方法可能不受欢迎有几个原因.
有些人坚持认为,如果扩展方法的this参数是,则应该抛出异常null.如果方法名称清楚,我不同意.
适用范围太广的扩展程序会使自动完成菜单变得混乱.这可以通过正确使用命名空间来避免,因此它们不会惹恼那些不想要它们的人.
我IEnumerable也尝试过这种方法,就像一个实验,看看我可以扭曲多少东西以适应Linq关键字,但我认为最终结果的可读性低于IfNotNull链接或原始命令式代码.
我最终得到了一个简单的自包含Maybe类,它有一个静态方法(不是扩展方法),对我来说效果非常好.但是,我和一个小团队一起工作,我的下一个最资深的同事对函数式编程和lambdas等感兴趣,所以他不会被它推迟.
Jon*_*eet 14
就像我是扩展方法的粉丝一样,我认为这不是真的有用.你仍然重复了表达式(在monadic版本中),它只是意味着你必须Maybe向每个人解释.在这种情况下,增加的学习曲线似乎没有足够的好处.
该IfNotNull版本至少设法避免重复,但我认为它仍然只是有点太长,没有实际更清楚.
也许有一天我们会得到一个零安全的解除引用运算符......
另外,我最喜欢的半邪恶扩展方法是:
public static void ThrowIfNull<T>(this T value, string name) where T : class
{
if (value == null)
{
throw new ArgumentNullException(name);
}
}
Run Code Online (Sandbox Code Playgroud)
这让你转过来:
void Foo(string x, string y)
{
if (x == null)
{
throw new ArgumentNullException(nameof(x));
}
if (y == null)
{
throw new ArgumentNullException(nameof(y));
}
...
}
Run Code Online (Sandbox Code Playgroud)
成:
void Foo(string x, string y)
{
x.ThrowIfNull(nameof(x));
y.ThrowIfNull(nameof(y));
...
}
Run Code Online (Sandbox Code Playgroud)
仍然有令人讨厌的重复参数名称,但至少它更整洁.当然,在.NET 4.0中我会使用Code Contracts,这就是我现在要写的内容...... Stack Overflow是一个很好的避免工作;)