我有这个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) + ";
答案 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;"
};
}