C# 可能是类和可选参数

C# 可能是类和可选参数,c#,optional-parameters,maybe,compile-time-constant,C#,Optional Parameters,Maybe,Compile Time Constant,我在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>

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

如果没有可用值,则where Else提供值。然而 这在理论上很好,但C#可选参数 必须是编译时常数,它完全 这种模式

有人能提出一个解决方案来保持 在这里不引入null或null的模式

我是否可以创建一个编译时常量来 在这里不代表任何将与我的以上工作
Maybe的实现?

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

double DoSomeCalc
(可能x=null
,可能y=null
)
{
x=x??可能。无();
y=y??可能。无();
this.X=X.Else(()=>CalculatedFaultx());
this.Y=Y.Else(()=>CalculatedFailure());
}

这并不理想,因为您必须在注释中的某个地方记录传递null意味着“使用特定的默认值”。

不,这里您无能为力。您的参数类型是引用类型,这意味着唯一可用的常量值是
null
和字符串文本。(显然,字符串文字在您的情况下没有用处;我只提到它们是唯一一种非null引用类型常量。)

一个选项是使
可能
成为一个结构而不是一个接口,默认值为“none”值。这将基本上与
Nullable
相同,但不受
T
必须是非null值类型的限制。然后,您可以使用:

void DoSomeCalc(Maybe<double> x = default(Maybe<double>),
                Maybe<double> y = default(Maybe<double>))
void DoSomeCalc(可能x=默认值(可能),
可能y=默认值(可能))
显示所有这些内容的示例代码:

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);
    }
}
使用系统;
结构可能
{
私有只读布尔值;
公共bool HasValue{get{return HasValue;}}
私有只读T值;
公共价值
{
得到
{
如果(!hasValue)
{
抛出新的InvalidOperationException();
}
返回值;
}
}
公共价值(T值)
{
this.hasValue=true;
这个值=值;
}
公共静态隐式运算符(T值)
{
返回新的可能(值);
}
}
课堂测试
{
静态void DoSomeCalc(可能x=默认值(可能),
可能y=默认值(可能))
{
Console.WriteLine(x.HasValue?“”+x.Value:“No x”);
Console.WriteLine(y.HasValue?“+y.Value:”无y”);
}
静态void Main()
{
Console.WriteLine(“第一次呼叫”);
剂量表(x:10);
Console.WriteLine(“第二次呼叫”);
剂量表(y:20);
}
}

显然,您希望向
添加更多功能,例如覆盖
到字符串
等于
,但您得到了一般的想法。当然,您仍然可以使用带有工厂方法的非泛型
类。

对我来说似乎是动态的……与动态无关。Maybe是静态类型。
params Maybe[]args
Maybe?@Corak破坏了名称的全部用途可选参数您为什么试图重新实现
Nullable
?破坏了使用Maybe的目的,不是吗?在这种情况下,我可以使用nullable。这取决于它的用途。传递null和传递不带值的null实例之间存在差异。nullable是一种结构(值类型),因此您实际上无法传递null。C#编译器会将null转换为nullable的正确构造函数。在您的例子中,您正在传递一个引用类型(接口),这意味着传递null实际上是有效的,并且由您来定义null可能意味着什么。Maybe的实现提供了比C#中的可空类型更多的功能。Maybe.None意味着我不想传递任何内容。它与实际传递null有不同的语义。那么传递null意味着什么呢?这和“我不想传递任何东西”不一样吗?我想区分调用方在编译时实际上没有在调用中包含参数和意外传入空引用。不幸的是,可能必须保留一个接口才能在t上启用协方差。该死的c只能在接口上具有协方差。没有到接口的隐式转换,现在这是编译时常量:(.然而,这篇文章似乎声称这是不可能的。向下滚动到
原因5:不可变类的初始化
@bradgonessurfing:Yup,这看起来不应该编译。我现在没有时间查看这篇文章,但有下载链接吗?我怀疑作者刚刚编写了他预期可以工作的代码,但没有tryi我看不到下载链接,但我猜你是对的,他没有尝试。@BradGonessurfing:事实上,它是在2009年10月编写的,VS2010直到2010年4月才发布,这意味着至少他没有在最终版本中测试它。我不认为有一个C#4编译器的预发布版本允许非compi可选参数的时间常数,但可能存在。这是这些严格限制问题的正确答案。
DoSomeCalc(y:20)
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() );
}
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);
    }
}