可能是类和可选参数

本文关键字:参数 | 更新日期: 2023-09-27 18:11:23

我在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)

如果None可用,则Else提供一个值。然而这在理论上都很好,但c#可选参数必须编译时间常数完全螺丝这种模式。

有谁能提出一个解决方案,将保持的意图不引入空值的模式?

无论如何,我可以创建一个编译时间常数代表这里没有,将与我的以上工作也许?

可能是类和可选参数

不行,你什么也做不了。您的参数类型是引用类型,这意味着唯一可用的常量值是null和字符串字面值。(显然,字符串字面量在你的情况下是没有用的;我只是将它们作为only一类非空引用类型常量。)

一种选择是使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>添加更多的功能,例如覆盖ToStringEquals,但是您得到了一般的想法。当然,您也可以使用工厂方法拥有非泛型Maybe类。

您可以在内部使用null来表示Maybe.None()。例如:

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意味着"使用特定的默认值"。