单元素元组和零元素元组有什么用?

What are single and zero element tuples good for?

C# 7.0 引入了值元组以及对它们的一些语言级别支持。它们 added the support 也是单个和零元素元组;但是,我无法找到它们何时有用的任何场景。

通过 ValueTuple.Create 重载,我可以创建任何类型的元组,但 C# 7.0 语法只允许至少两个元素:

Microsoft (R) Roslyn C# Compiler version 2.8.3.62923
Loading context from 'CSharpInteractive.rsp'.
Type "#help" for more information.
> ValueTuple.Create()
[()]
> ValueTuple.Create(1)
[(1)]
> ValueTuple.Create(1, "x")
[(1, x)]

通过元组语法:

> var t2 = (1, 2);
> var t1 = (1); // t1 is now int (of course)
> ValueTuple<int> t1 = (1);
(1,23): error CS0029: Cannot implicitly convert type 'int' to 'ValueTuple<int>'
> ValueTuple<int> t1 = new ValueTuple<int>(1);
> t1
[(1)]

我想我找到了 this feature was requested but none of the code samples are valid in C# now and could not find any references in the planned features of C# 8.0 的线程,甚至在递归元组模式中也没有。

在请求中提到了线程函数式编程语言。是否有现在使用它们的函数式语言?我不是 F# 专家,但它的元组 reference 没有提到任何单个和零元素元组的使用。

所以 TL;DR 问题:

我想不出一个元素元组的用例。编辑——正如所指出的,引用的页面提到了一个元素元组作为命名 return 参数的一种方式,这听起来实际上很有用,尽管这可能是特定于 C# 的。

零元素元组在函数式语言中也称为 unit。它有点等同于 C# 中的 void,某些(大多数?)函数式语言没有。

不同之处在于 () 是一个实际值,您可以用它来做一些事情,比如保存列表、模式匹配等。函数式语言需要这个,因为一个函数必须 return 一些东西,如果你想要的话要 return "nothing" 你必须通过 returning () 明确地做到这一点。您还可以编写一个接受 unit 作为参数的函数,这基本上意味着它是一个延迟执行的值,很可能有副作用:

let x = getFromDb() // will read DB
stream.Flush() // will change state

0 元组有什么用?

二元组或三元组表示一组相关项。 (2D 中的点 space、颜色的 RGB 值等)一元组不是很有用,因为它可以很容易地用单个 int.

替换

0 元组似乎更无用,因为它完全不包含任何内容。然而,它具有的特性使其在 F# 等函数式语言中非常有用。例如,0元组类型只有一个值,通常表示为()。所有 0 元组都有这个值,所以它本质上是一个单例类型。在大多数函数式编程语言中,包括 F#,这称为 unit 类型。

C# 中 return void 的函数将 return F# 中的 unit 类型:

let printResult = printfn "Hello"

运行 在 F# 交互式解释器中,您将看到:

val printResult : unit = ()

这意味着值 printResult 的类型为 unit,并且值为 ()(空元组,unit 的唯一值类型)。

函数也可以将 unit 类型作为参数。在 F# 中,函数可能看起来没有参数。但实际上,他们采用的是 unit 类型的单个参数。这个函数:

let doMath() = 2 + 4

实际上等同于:

let doMath () = 2 + 4

也就是说,一个函数接受一个 unit 类型的参数并且 return 是 int 值 6。如果您查看 F# 交互式解释器打印的类型签名当你定义这个函数时,你会看到:

val doMath : unit -> int

事实上,所有函数都将至少接受一个参数和 return 一个值,即使该值有时是一个 "useless" 值,如 (),这意味着函数组合是在 F# 中比在没有 unit 类型的语言中容易得多。但这是一个更高级的主题,我们稍后会谈到。现在,请记住,当您在函数签名中看到 unit 或在函数参数中看到 () 时,这就是 0 元组类型,表示 "This function takes, or returns, no meaningful values."

C# 中不支持 o 元组 (nople) 和 1 元组 (oneple)。

但是,在基础 class 库 (BCL) 上,为了连贯性以及以防万一将来有人发现它的用途,有 ValueTupleValueTuple<T>。例如,具有 0 元组和 1 元组结构的语言。

ValueTuple 有一些附加值。来自 the documentation:

The ValueTuple structure represents a tuple that has no elements. It is useful primarily for its static methods that let you create and compare instances of value tuple types. Its helper methods let you instantiate value tuples without having to explicitly specify the type of each value tuple component. By calling its static Create methods, you can create value tuples that have from zero to eight components. For value tuples with more than eight components, you must call the ValueTuple constructor.

目前,ValueTuple'1 仅用于长元组的表示,即使没有用于 1 元组的 C# 元组语法:ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>> 是 8 元组的基础类型.

至于0元组和ValueTuple'0,我们推测它们可能会出现在未来的场景中(如果我没记错的话,可能是一些递归模式)。

1 元组可用作可空值的非空包装器。这在尝试添加到字典时很有用,例如:

void Process<TKey>(TKey[] keys)
{
    var dict = new Dictionary<TKey, Something>();
    var firstKey = keys[0];
    dict[firstKey] = new Something();
    ...
}
如果启用 nullable(创建 dict 时),

将显示警告,因为 TKey 可能是可空类型(API 可能愿意接受) 而 Dictionary<TKey, TValue> 不接受 null ;同时:

void Process<TKey>(TKey[] keys)
{
    var dict = new Dictionary<ValueTuple<TKey>, Something>();
    var firstKey = ValueTuple.Create(keys[0]);
    dict[firstKey] = new Something();
    ...
}

不会显示警告。

1 元组可用作委托的单个参数列表。元组很适合用作委托参数列表,因为您可以更改它们的结构而不破坏委托的实现。您也可以通过这种方式命名委托的参数。但是因为你不能使用 1-Tuple,如果参数的数量变为 1,你必须恢复到常规参数。

例如:

int DoSomething(
   Func<(int First, int Second, int Third), int> function
) =>
   function((1, 2, 3));

// notice that you could add more parameters or change the order
// of the values and not break the lambda
DoSomething(args => 
   args.First + args.Second - args.Third
);

// DOESN'T WORK
int DoSomethingElse(
   Func<(int First), int> function
) =>
   function((1));

// does work but not as nice
int DoSomethingElse2(
   Func<int, int> function
) =>
   function(1);

单值元组可用于描述方法的return值。 有时提供有关 return 值的附加信息很有用,例如:

  (int ScreenNumber) AddingTarget(string stockItemCode);