【问题标题】:Implementing monadic bind for Nullable<> *and* reference types为 Nullable<> *and* 引用类型实现一元绑定
【发布时间】:2012-07-09 19:22:27
【问题描述】:

我有这个功能:

public static U? IfNotNull<T, U>(this T? self, Func<T, U?> func)
    where T : struct
    where U : struct
{
    return (self.HasValue) ? func(self.Value) : null;
}

例子:

int? maybe = 42;
maybe.IfNotNull(n=>2*n); // 84

maybe = null;
maybe.IfNotNull(n=>2*n); // null

我希望它适用于隐式可空引用类型以及显式 Nullable&lt;&gt; 类型。这个实现会起作用:

public static U IfNotNull<T, U>(this T? self, Func<T, U> func)
    where T : struct
    where U : class
{
    return (self.HasValue) ? func(self.Value) : null;
}

当然,重载决议不考虑类型约束,所以你不能同时拥有两者。有解决办法吗?

【问题讨论】:

  • 不能去掉where子句吗?
  • 您可以简单地重命名第二个函数。如果问题出在不同的返回类型...
  • 否:第一个函数中的U 必须声明为structNullable'd,并且第二个函数中的U 必须声明为class,这样null 可以作为 U 返回。

标签: c# extension-methods nullable monads


【解决方案1】:

当然,重载解析不考虑类型约束

确实如此……但不是方法本身的类型约束。它查看 parameter 类型的类型约束。

在 C# 4(具有可选参数)中,您可以这样做...但我真的建议您不要这样做:

public class MustBeStruct<T> where T : struct {}
public class MustBeClass<T> where T : class {}

public static U? IfNotNull<T, U>(this T? self, Func<T, U?> func,
                       MustBeStruct<U> ignored = default(MustBeStruct<U>))
    where T : struct
    where U : struct
{
    return (self.HasValue) ? func(self.Value) : null;
}

public static U IfNotNull<T, U>(this T? self, Func<T, U> func,
                                MustBeClass<U> ignored = null)
    where T : struct
    where U : class
{
    return (self.HasValue) ? func(self.Value) : null;
}

请参阅this blog post 了解有关这种可怕的骇人攻击的更多详细信息。

就我个人而言,我可能只是以不同的方式命名这两种方法,这样重载解析就不需要那么辛苦了——你的代码的读者也不需要。

【讨论】:

  • 这是我一段时间以来看到的最好最差的答案。我印象深刻,也感到震惊。
【解决方案2】:

所以我最终得到了这个:

public static U IfNotNull<T, U>(this T self, Func<T, U> func)
    where U : class
{
    return (self != null)
        ? func(self)
        : (U)null;
}

public static U? IfNotNull<T, U>(this T self, Func<T, U?> func)
    where U : struct
{
    return (self != null)
        ? (U?)func(self)
        : null;
}

重载解析器似乎对此很满意。对于Nullable&lt;&gt; 类型来说,这是一点点额外的工作:

object o = null;
o.IfNotNull(x => x.ToString());
o.IfNotNull(x => x.GetHashCode() as int?);

int? i = null;
i.IfNotNull(x => Math.Abs(x.Value).ToString());
i.IfNotNull(x => Math.Abs(x.Value) as int?);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-18
    • 2011-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    相关资源
    最近更新 更多