我在c#中实现了一个Maybe / Option类。基本实现是
public delegate Maybe<TOutput> Converter<in TInput, out TOutput>(TInput input);
public delegate TOutput ElseDelegate<out TOutput>();
public delegate Maybe<TOutput> ElseDelegate2<out TOutput>();
public interface Maybe<out TResult> : IEnumerable<TResult>
{
Maybe<B> Bind<B>(Converter<TResult, B> f);
TResult Value();
bool IsSome();
}
public static class Maybe
{
public static Maybe<T> None<T>()
{
return new None<T>();
}
}
public interface INone<out TResult> : Maybe<TResult>
{
}
public interface ISome<out TResult> : Maybe<TResult>
{
}
public struct None<TResult> : INone<TResult>
{
public IEnumerator<TResult> GetEnumerator()
{ yield break; }
IEnumerator IEnumerable.GetEnumerator()
{ yield break; }
public bool IsSome() { return false; }
public Maybe<TOutput> Bind<TOutput>(Converter<TResult, TOutput> f)
{
return new None<TOutput>();
}
public TResult Value()
{
throw new IndexOutOfRangeException("None has no value");
}
}
public struct Some<TResult> : Maybe<TResult>
{
private TResult _Value;
public Some(TResult value)
{
_Value = value;
}
public IEnumerator<TResult> GetEnumerator()
{ yield return _Value; }
IEnumerator IEnumerable.GetEnumerator()
{ yield return _Value; }
public bool IsSome() { return true; }
public Maybe<TOutput> Bind<TOutput>(Converter<TResult, TOutput> f)
{
return f(_Value);
}
public TResult Value()
{
return this._Value;
}
}
#endregion
有一堆扩展方法,我没有在这里包含。这一切都有效 精细。但是我想要实现的标准模式如下, 使用Maybe实现可选参数默认值,如F#
void DoSomeCalc
( Maybe<double> x = Maybe.None<double>()
, Maybe<double> y = Maybe.None<double>()
)
{
this.X = x.Else( ()=> CalculateDefaultX() );
this.Y = y.Else( ()=> CalculateDefaultY() );
}
所以我可以做到
DoSomeCalc(x:10)
或
DoSomeCalc(y:20)
如果无可用,则Else提供值。然而 这在理论上都很好,但是C#可选参数 必须是编译完全拧紧的时间常数 这种模式。
任何人都可以建议一个能够保持意图的修复程序 这种模式没有在这里引入nullables或null?
无论如何,我可以创建一个编译时常量 代表没有在这里将与我的上述工作 执行Maybe?
答案 0 :(得分:6)
不,你在这里无能为力。您的参数类型是引用类型,这意味着唯一可用的常量值是null
和字符串文字。 (显然字符串文字在你的情况下没用;我只提到它们是唯一的种非空引用类型常量。)
一个选项是使Maybe<T>
成为结构而不是接口,默认值为“none”。这将基本上与Nullable<T>
相同,但没有约束T
必须是不可为空的值类型。然后你可以使用:
void DoSomeCalc(Maybe<double> x = default(Maybe<double>),
Maybe<double> y = default(Maybe<double>))
显示所有这些的示例代码:
using System;
struct Maybe<T>
{
private readonly bool hasValue;
public bool HasValue { get { return hasValue; } }
private readonly T value;
public T Value
{
get
{
if (!hasValue)
{
throw new InvalidOperationException();
}
return value;
}
}
public Maybe(T value)
{
this.hasValue = true;
this.value = value;
}
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>(value);
}
}
class Test
{
static void DoSomeCalc(Maybe<double> x = default(Maybe<double>),
Maybe<double> y = default(Maybe<double>))
{
Console.WriteLine(x.HasValue ? "x = " + x.Value : "No x");
Console.WriteLine(y.HasValue ? "y = " + y.Value : "No y");
}
static void Main()
{
Console.WriteLine("First call");
DoSomeCalc(x: 10);
Console.WriteLine("Second call");
DoSomeCalc(y: 20);
}
}
显然,您希望向Maybe<T>
添加更多功能,例如覆盖ToString
和Equals
,但您会得到一般的想法。当然,您仍然可以使用工厂方法的非通用Maybe
类。
答案 1 :(得分:0)
您可以在内部使用null表示Maybe.None&lt; double&gt;()。 E.g:
double DoSomeCalc
( Maybe<double> x = null
, Maybe<double> y = null
)
{
x = x ?? Maybe.None<double>();
y = y ?? Maybe.None<double>();
this.X = x.Else( ()=> CalculateDefaultX() );
this.Y = y.Else( ()=> CalculateDefaultY() );
}
这不是理想的,因为您必须在注释中的某处记录传递null意味着“使用特定的默认值”。