为Nullable实现monadic绑定<> *和*引用类型

时间:2012-07-09 19:22:27

标签: c# extension-methods nullable monads

我有这个功能:

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<>类型。这种实现可行:

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;
}

但是当然重载分辨率不会考虑类型约束,因此您不能同时使用两者。有解决方案吗?

2 个答案:

答案 0 :(得分:7)

  

但当然,重载决策不会考虑类型约束

它确实......但不是方法本身的类型约束。它查看参数类型的类型约束。

在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

就我个人而言,我可能只是将两种方法命名为不同,以便重载解析不需要如此努力 - 而且代码的读者也没有。

答案 1 :(得分:0)

所以我最终得到了这个:

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<>类型来说,这是一小部分额外的工作:

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?);