C# 如何在Maybe和Nullable的相同名称下实现映射和绑定操作?

C# 如何在Maybe和Nullable的相同名称下实现映射和绑定操作?,c#,generics,C#,Generics,我有一个可能类型,我在代码I控件中使用它作为可空的等效引用类型 定义是struct,可能在T:class的地方,它有通常的HasValue和Value方法 我想同时提供Map和Bind操作(与F#and的行为相同),并且我想以相同的名称IfHasValue实现这两个操作 我想实现IfHasValue()不仅是为了Maybe,也是为了Nullable,并且允许从类到结构的映射(和绑定),反之亦然 基本上,我希望编译并通过所有这些测试: var maybeValue = Maybe.WithValu

我有一个
可能
类型,我在代码I控件中使用它作为
可空
的等效引用类型

定义是
struct,可能在T:class
的地方,它有通常的
HasValue
Value
方法

我想同时提供
Map
Bind
操作(与F#and的行为相同),并且我想以相同的名称
IfHasValue
实现这两个操作

我想实现
IfHasValue()
不仅是为了
Maybe
,也是为了
Nullable
,并且允许从类到结构的映射(和绑定),反之亦然

基本上,我希望编译并通过所有这些测试:

var maybeValue = Maybe.WithValue("some string");
int? nullableValue = 42;

maybeValue.IfHasValue(x => x.Length).ShouldEqual(11);
maybeValue.IfHasValue(x => (int?)x.Length).ShouldEqual(11);

maybeValue.IfHasValue(x => x + " more").Value.ShouldEqual("some string more");
maybeValue.IfHasValue(x => Maybe.WithValue(x + " more")).Value.ShouldEqual("some string more");

nullableValue.IfHasValue(x => x + 1).ShouldEqual(43);
nullableValue.IfHasValue(x => (int?)x + 1).ShouldEqual(43);

nullableValue.IfHasValue(x => x.ToString(CultureInfo.InvariantCulture)).Value.ShouldEqual("42");
nullableValue.IfHasValue(x => Maybe.WithValue(x.ToString(CultureInfo.InvariantCulture))).Value.ShouldEqual("42");
我遇到的问题是在这种情况下

maybeValue.IfHasValue(x => Maybe.WithValue(x + " more")).Value.ShouldEqual("some string more");
类型推断无法确定这两个重载中的哪一个是预期的

public static Maybe<TOut> IfHasValue<TIn, TOut>(this Maybe<TIn> maybe, Func<TIn, Maybe<TOut>> selector)
    where TIn : class where TOut : class {}

// Note that in the full implementation I use the delegate trick to avoid
// this overload conflicting with the `TOut : class` version, but I omitted it
// since it's not needed for the overloads under discussion here.
public static TOut? IfHasValue<TIn, TOut>(this Maybe<TIn> maybe, Func<TIn, TOut> selector)
    where TIn : class where TOut : struct {}
public static Maybe IfHasValue(此Maybe Maybe,Func选择器)
where-TIn:class-where-TOut:class{}
//注意,在完整的实现中,我使用委托技巧来避免
//这个重载与'TOut:class'版本冲突,但我忽略了它
//因为这里讨论的重载不需要它。
公众静态兜售?IfHasValue(这可能是,Func选择器)
where-TIn:class-where-TOut:struct{}
因为我的
Maybe
类型是一个结构,并且我不能通知编译器,如果选择器的结果是
Maybe
,那么应该始终使用
Maybe
(首先)重载,因为
可空的
是冗余的

有趣的是,编译器似乎已经为
Nullable
()这样做了:

其中T:struct类型参数必须是值类型。可以指定除Nullable之外的任何值类型。有关更多信息,请参阅使用可为空的类型(C#编程指南)

但我认为这是一个老生常谈的问题,没有办法让它以同样的方式对待我的
输入

在这种情况下,有什么方法可以让类型推断工作吗

我考虑过但不太满意的变通方法:

  • 映射
    绑定
    版本使用不同的名称
  • 在这种情况下需要显式指定类型参数

限制为引用类型没有意义,您应该使其适用于任何类型,并提供与可空结构的转换方法。这是一个有趣的观点。我们目前将
Maybe
类型限制为引用类型的理由是,值类型已经有了
Maybe
类型,
Nullable
,它比我们的
Maybe
类型好,因为它是语言的一部分,编译器也知道它:语法更好,其他库也使用它,它提升了普通运算符等。因此,仅仅因为它不支持引用类型就放弃它似乎是一种耻辱。如果我的问题有什么不清楚的地方,请让我知道它是什么,我会改进它。我试图非常清楚地说明我要做什么,为什么,以及遇到的问题,所以我不确定混淆的地方是什么。问题是
Nullable
不是一个“可能用于值类型”-它没有map/bind/join操作符,并且由编译器专门处理。您还必须将
T:class
约束传播到您希望在中使用
值的每个方法中。如果您要创建自己的maybe类型,我建议您在代码中的任何地方都使用它,并且只在边界处转换为可为null的类型。泛型约束不用于重载解析,因此在这里尝试执行的操作将给您带来问题。