C# 返回命名ValueTuple和成功标志的函数
我一次又一次地遇到这种情况,我希望函数同时返回命名的ValueTuple和成功标志 我能想到的所有选择都不是很好:( 选项1:C# 返回命名ValueTuple和成功标志的函数,c#,c#-7.0,c#-8.0,C#,C# 7.0,C# 8.0,我一次又一次地遇到这种情况,我希望函数同时返回命名的ValueTuple和成功标志 我能想到的所有选择都不是很好:( 选项1: bool MyFunc(T1 param, out (Type1 name1, Type2 name2)? result) if (!MyFunc(param, out var result)) { .. .. my error handling } .. do stuff with `result.Value.name1` and `result.Value.na
bool MyFunc(T1 param, out (Type1 name1, Type2 name2)? result)
if (!MyFunc(param, out var result)) { .. .. my error handling }
.. do stuff with `result.Value.name1` and `result.Value.name2`
bool MyFunc(T1 param, out (Type1 name1, Type2 name2)? result)
if (!MyFunc(param, out var result)) { .. .. my error handling }
var (name1, name2) = result.Value;
.. do stuff with name1, name2
丑陋,需要定义结果,到处都需要result.Value
选项2:
bool MyFunc(T1 param, out (Type1 name1, Type2 name2)? result)
if (!MyFunc(param, out var result)) { .. .. my error handling }
.. do stuff with `result.Value.name1` and `result.Value.name2`
bool MyFunc(T1 param, out (Type1 name1, Type2 name2)? result)
if (!MyFunc(param, out var result)) { .. .. my error handling }
var (name1, name2) = result.Value;
.. do stuff with name1, name2
因为需要定义结果并显式破坏元组,所以很难看
选项3:
(Type1 name1, Type2 name2)? MyFunc(Type3 param) {}
var myFuncRes = myFunc(param);
if (myFuncRes is null) { .. my error handling }
.. do stuff with myFuncRes.Value.name1 etc
因为需要myFuncRes
和.Value
或显式破坏元组而变得丑陋
我希望能够做到的是:
(Type1 name1, Type2 name2)? MyFunc(T1 param)
if (var myFuncRes = MyFunc(param) is null) { .. .. my error handling }
.. do stuff with `myFuncRes.name1` and `myFuncRes.name2` directly !
这是我明确的意图……但这是不允许的
有更好的方法或共同的解决方案吗 (注意我不想在这里抛出异常)您可以:
(Type1 name1, Type2 name2)? MyFunc() {...}
if (!(MyFunc() is {} myFuncRes))
{
// Error handling
return;
}
// use myFuncRes.name1 and myFuncRes.name2
这使用了。{}
是一个属性模式,它实际上没有指定任何属性,因此它匹配任何不为null的对象
不过,IMO认为它的可读性不是很好。有人讨论过如何添加新模式来改进这样的情况,例如不为null
还有一种方法可以改进在out声明中分解元组的情况,它可以让您编写:
bool MyFunc(out (Type1 name1, Type2 name2) result) {...}
if (!MyFunc(out var (name1, name2))
{
}
你可以做:
(Type1 name1, Type2 name2)? MyFunc() {...}
if (!(MyFunc() is {} myFuncRes))
{
// Error handling
return;
}
// use myFuncRes.name1 and myFuncRes.name2
这使用了。{}
是一个属性模式,它实际上没有指定任何属性,因此它匹配任何不为null的对象
不过,IMO认为它的可读性不是很好。有人讨论过如何添加新模式来改进这样的情况,例如不为null
还有一种方法可以改进在out声明中分解元组的情况,它可以让您编写:
bool MyFunc(out (Type1 name1, Type2 name2) result) {...}
if (!MyFunc(out var (name1, name2))
{
}
为什么不使用嵌套元组呢
static ((int name1, int name2) result, bool success) Test()
{
return ((1, 1), true);
}
static void Main()
{
var x = Test();
if (x.success)
{
Console.WriteLine("Your numbers are {0} and {1}", x.result.name1, x.result.name2);
}
}
为什么不使用嵌套元组呢
static ((int name1, int name2) result, bool success) Test()
{
return ((1, 1), true);
}
static void Main()
{
var x = Test();
if (x.success)
{
Console.WriteLine("Your numbers are {0} and {1}", x.result.name1, x.result.name2);
}
}
设计用于表示一组变量(如函数中的局部变量),而不是实体
如果将元组视为一个单元,那么很可能是做错了什么
最初的方法首先应该是:
bool MyFunc(T1 param, out Type1 name1, out Type2 name2)
(bool success, Type1 name1, Type2 name2) MyFunc(T1 param)
如果不能使用out参数(例如,如果它是异步方法),则应为:
bool MyFunc(T1 param, out Type1 name1, out Type2 name2)
(bool success, Type1 name1, Type2 name2) MyFunc(T1 param)
设计用于表示一组变量(如函数中的局部变量),而不是实体
如果将元组视为一个单元,那么很可能是做错了什么
最初的方法首先应该是:
bool MyFunc(T1 param, out Type1 name1, out Type2 name2)
(bool success, Type1 name1, Type2 name2) MyFunc(T1 param)
如果不能使用out参数(例如,如果它是异步方法),则应为:
bool MyFunc(T1 param, out Type1 name1, out Type2 name2)
(bool success, Type1 name1, Type2 name2) MyFunc(T1 param)
返回这些结果的典型方法是:
(bool success, string name1, string name2) MyFunc(bool param)
{
return param ? (true,"A","B")
:default;
}
bool
的默认值为false,因此我可以懒散地返回default
您可以将模式匹配与is
一起使用,以检查和提取单个干净行中的值:
if(MyFunc(true) is (true,var name1,var name2)){
Console.WriteLine("AAA");
}
if(MyFunc(false) is (true,var name3,var name4)){
Console.WriteLine("Should never enter here");
}
这将只打印“AAA”
围棋风格
Go对多值结果使用元组的方式与目前相同,因为它还没有异常。这会导致复杂的控制流,并引入忽略错误的可能性。但是,按照惯例,最后一个元组字段不是一个标志,而是一条错误消息:
func f1(arg int) (int, error) {
if arg == 42 {
return -1, errors.New("can't work with 42")
}
return arg + 3, nil
}
...
if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e)
} else {
fmt.Println("f1 worked:", r)
}
通过在元组中添加error
字段并匹配null,可以在C#中完成相同的操作:
(int i,string? error) F1(int arg)
{
return arg==42? (-1,"Can't work with 42")
:(arg+3,null);
}
...
var (i,error) =F1(42);
if (error is string err)
{
Console.WriteLine($"F1 failed: {err}");
}
else
{
Console.WriteLine($"F1 worked: {i}");
}
或
或者,使用开关表达式:
var output = F1(43) switch {
(_,string error)=>$"F1 failed: {error}",
(int i,null) =>$"F1 worked: {i}"
};
Console.WriteLine(output);
返回这些结果的典型方法是:
(bool success, string name1, string name2) MyFunc(bool param)
{
return param ? (true,"A","B")
:default;
}
bool
的默认值为false,因此我可以懒散地返回default
您可以将模式匹配与is
一起使用,以检查和提取单个干净行中的值:
if(MyFunc(true) is (true,var name1,var name2)){
Console.WriteLine("AAA");
}
if(MyFunc(false) is (true,var name3,var name4)){
Console.WriteLine("Should never enter here");
}
这将只打印“AAA”
围棋风格
Go对多值结果使用元组的方式与目前相同,因为它还没有异常。这会导致复杂的控制流,并引入忽略错误的可能性。但是,按照惯例,最后一个元组字段不是一个标志,而是一条错误消息:
func f1(arg int) (int, error) {
if arg == 42 {
return -1, errors.New("can't work with 42")
}
return arg + 3, nil
}
...
if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e)
} else {
fmt.Println("f1 worked:", r)
}
通过在元组中添加error
字段并匹配null,可以在C#中完成相同的操作:
(int i,string? error) F1(int arg)
{
return arg==42? (-1,"Can't work with 42")
:(arg+3,null);
}
...
var (i,error) =F1(42);
if (error is string err)
{
Console.WriteLine($"F1 failed: {err}");
}
else
{
Console.WriteLine($"F1 worked: {i}");
}
或
或者,使用开关表达式:
var output = F1(43) switch {
(_,string error)=>$"F1 failed: {error}",
(int i,null) =>$"F1 worked: {i}"
};
Console.WriteLine(output);
如果返回值为false,则元组不应用于任何内容,则这种方法可能会起作用。当这种情况发生时,我们只提供一个默认元组(应立即忽略),而不是试图通过可为null的类型来传达它为null
static bool Test(T1 param, out (Type1 name1, Type2 name2) result)
{
try
{
//Successful case
result = ( X, Y );
return true;
}
catch
{
//Failure case
result = (default(Type1), default(Type1));
return false;
}
}
现在,调用方需要编写的全部内容是:
if (Test(param, out var result))
{
Console.WriteLine("Your results were {0} and {1}", result.name1, result.name2);
}
如果返回值为false,则元组不应用于任何内容,则这种方法可能会起作用。当这种情况发生时,我们只提供一个默认元组(应立即忽略),而不是试图通过可为null的类型来传达它为null
static bool Test(T1 param, out (Type1 name1, Type2 name2) result)
{
try
{
//Successful case
result = ( X, Y );
return true;
}
catch
{
//Failure case
result = (default(Type1), default(Type1));
return false;
}
}
现在,调用方需要编写的全部内容是:
if (Test(param, out var result))
{
Console.WriteLine("Your results were {0} and {1}", result.name1, result.name2);
}
我确实找到了一种更好的感觉:
(bool success, (string name1, string name2)) MyFunc() {
if (DateTime.Now.Year > 2020) return (false, ("", "")); // error
return (true, ("some value", "some value"));
}
void F() {
if (!(MyFunc() is (true, var (name1, name2)))) return;
Console.WriteLine(name1);
.. do stuff with name1 and name2
}
这相当清晰和简洁,没有引入任何不必要的变量我确实找到了一种更好的方法:
(bool success, (string name1, string name2)) MyFunc() {
if (DateTime.Now.Year > 2020) return (false, ("", "")); // error
return (true, ("some value", "some value"));
}
void F() {
if (!(MyFunc() is (true, var (name1, name2)))) return;
Console.WriteLine(name1);
.. do stuff with name1 and name2
}
这相当清晰简洁,不会引入任何不必要的变量这是元组中的成功标志部分吗?似乎你也想要吃蛋糕。你可以用一个单独的元组来包装你的名称1/2元组,该元组还包含结果,但这仍然需要进行分解。看看是否包含成功标志您可以将模式匹配与success常量一起使用,例如if(MyFunc()为(true,var someValue)){usethit(someValue);}
成功标志是元组的一部分吗?似乎你也想吃蛋糕。你可以用一个单独的元组来包装你的name1/2元组,该元组还包括result,但这仍然需要解构。看看是否包含成功标志,你可以使用模式匹配成功常量,例如If(MyFunc()是(true,var someValue){usethit(someValue);}
感谢您的扩展。模式匹配的有趣用法…我必须看看它。这似乎并不完全正确