如何访问泛型类中数组中的项?

时间:2019-06-13 14:56:38

标签: c#

我有这个csharp代码;

namespace ConsoleApp1
{

    interface Option<a>
    {
        b Visit<b>(Func<a, b> on_some, Func<b> on_none);
    }
    class None<a> : Option<a>{
        public None(){}

        public b Visit<b>(Func<a, b> on_some, Func<b> on_none)
        {
            return on_none();
        }
    }

    class Some<a> : Option<a>
    {
        public a v;

        public Some(a v) {
            this.v = v;
        }

        public b Visit<b>(Func<a, b> on_some, Func<b> on_none)
        {
            return on_some(v);
        }
    }

    class Simple{
        public static void Main(string[] args)
        {
            var values_or_not = new Option<int>[]
            {
                new None<int>(),
                new Some<int>(10),
                new Some<int>(2),
                new None<int>()
            };
            var s = "";
            for (int i = 0; i < values_or_not.Length; i = i + 1)
            {
                var x = values_or_not[i];
                Func<int, string> onSome = v => "Values: ";
                Func<string> onNone = () => "No Value";
                s = s + values_or_not[i].Visit<string>(onSome, onNone) + "; ";
                Console.WriteLine(s);
            }
        }
    }
}

我希望最终的字符串输出看起来像这样;

  

没有价值;值:10;值:2;没有价值;

因此,需要将给定的值放入输出字符串中。最好在这三行中;

                Func<int, string> onSome = v => "Values: ";
                Func<string> onNone = () => "No Value";
                s = s + values_or_not[i].Visit<string>(onSome, onNone) + "; 

2 个答案:

答案 0 :(得分:0)

您面临的问题与数组无关。由于字段v仅包含在Some<a>中,因此您不能从Option<a>对象访问它。

onSome委托更改为

Func<int, string> onSome = v => $"Value: {v}";

Console.WriteLine移出循环。

string s = "";
Func<int, string> onSome = v => $"Value: {v}";
Func<string> onNone = () => "No Value";
foreach (Option<int> option in values_or_not) {
    s += option.Visit<string>(onSome, onNone) + "; ";
}
Console.WriteLine(s);

此外,您可以在循环之前分配委托,因为它们不会更改。在onSome中,v只是其值尚未定义的参数。将为它分配实际值,并在调用委托时评估$"Value: {v}"

使用foreach循环比使用for循环要容易一些,因为它不需要索引变量和索引数组访问。请注意,您也可以用本地函数替换委托。

string s = "";

string onSome(int v) => $"Value: {v}";
string onNone() => "No Value";

foreach (Option<int> option in values_or_not) {
    s += option.Visit(onSome, onNone) + "; ";
}
Console.WriteLine(s);

另一种选择是检查循环中的类型;但是,这不是一个好的面向对象方法。

答案 1 :(得分:0)

我假设实际的问题是如何模拟F#的已区分联合并为不同类型编写不同的东西。您不需要Visit方法。在C#7和更高版本中,您可以使用模式匹配,前提是两种类型都有一个通用的基本类型,可以很容易地成为一个空接口,例如:

interface IOption<T> {}

public class None<T>:IOption<T>{}

public class Some<T>:IOption<T>
{
    public T Value {get;}

    public Some(T value)
    {
        Value=value;
    }

}

然后:

 var values = new IOption<int>[]
        {
            new None<int>(),
            new Some<int>(10),
            new Some<int>(2),
            new None<int>()
        };
 var str="";
 foreach(var it in values)
 {
     switch(it)
     {
         case Some<int> s:
             str+=$"Values: {s.Value};";
             break;
         case None<int> _:
             str+="No Value;";
             break;                 
     }
 }

C#8并切换表达式

C#8带来了开关表达式,使您可以使用与F#中几乎相同的语法:

 foreach(var it in values)
 {
     str+= it switch {
         Some<int> s => $"Values: {s.Value};",
         None<int> _=>"No Value;"
     };
 }