【问题标题】:Name ValueTuple properties when creating with new使用 new 创建时命名 ValueTuple 属性
【发布时间】:2017-09-19 19:34:32
【问题描述】:

我知道我可以在创建元组时隐式命名参数,例如:

var me = (age: 21, favoriteFood: "Custard");

是否可以在显式创建元组时命名参数?即

var me = new ValueTuple<int, string>(21, "Custard");

【问题讨论】:

标签: c# .net c#-7.0


【解决方案1】:

不,你不能。 ValueTuple 类型实际上独立于 C# 中的命名字段支持。后者更像是匿名类型的命名属性。也就是说,编译器分析代码并根据您的声明和用法为适当的成员生成别名。编译器通过赋值来学习字段的名称。由于基本构造函数语法不提供命名字段的机制,因此您不能使用它直接生成具有命名字段的元组。

当然,您可以通过多种方式重新解释从构造函数语法返回的值,以将名称分配给该返回值。我假设您知道这种方法并且正在寻找更直接的方法。

作为我所说的“重新解释”的一个例子,你可以这样做:

static (int value, string text) ConvertToNamed((int, string) t) => t;

然后这将在新变量中命名字段:

var t1 = new ValueTuple<int, string>(21, "hello");
var t2 = ConvertToNamed(t1);

变量t1Item1Item2 卡住。但编译器会为变量t2隐式生成所需的名称。

也许一个更好的例子是你不需要额外的方法:

(int value, string text) t = new ValueTuple<int, string>(21, "hello");

同样,您并没有真正在构造函数语法中命名字段,而是通过局部变量声明重新解释它们。

这可能不是一个严重的限制。在希望有一个持久的、易于分配的名称的情况下,声明一个用户定义的类型可能比使用元组语法更好。您也可以为用户定义的类型编写解构器,并且声明这样的类型意味着名称是反射时的一等公民,dynamic 等。

【讨论】:

  • 你是说可以在声明后设置名称?
  • 不是那个变量,不。但是您可以通过其他设置名称的语法来传递值。我将在上面添加一个示例。
  • 我想我知道你在说什么。谢谢。
  • 有什么理由为什么方法是(int value, string text) ConvertToNamed((int value, string text) t) 而不仅仅是(int value, string text) ConvertToNamed((int, string) t)
  • @svick:习惯的力量?不,我想不会。 :) 该方法不使用任何元组字段,因此当然没有任何理由命名它们。没有它也能正常工作。
【解决方案2】:

您可以在赋值期间强制转换为命名元组:

var me = ((int value, string text)) new ValueTuple<int, string>(21, "Custard");

【讨论】:

  • 请解释该语法如何优于(age: 21, favoriteFood: "Custard")(即实现相同目的的通常、自然的方式)。
  • 基本上,当命名元组的语法糖仍然不可接受时,如 EF lambda 操作,即 Join、Select 等,此变体很有用。
  • 请举个例子。我不知道在任何情况下“命名元组的语法糖仍然不可接受”,在处理 LINQ 运算符时当然不是。
  • 这是我代码中的一个示例:_dbContext.Products .Where(p=&gt;p.Id == productId) .Join(ProductDetails, p=&gt;p.Id, pd=&gt;pd.Id, (p, pd) =&gt;((Product Product, ProductDetail Detail)) new ValueTuple&lt;Product, Detail&gt;(p, pd)) .ProjectTo&lt;ProductWithDetailsDTO&gt;(_mapper.ConfigurationProvider).ToListAsync(cancellationToken); 实际上,除了那个之外,我没有找到将 2 个连接表映射到 1 个 DTO 的工作方法。
【解决方案3】:

在 Linq 中使用 ValueTuple 时正在寻找这个。我做了一个 F2-rename 变量,它创建了以下语法:

var query = from x in myList
select (First: x.something, Second: x.other) into g
select new Whatever(g.First, g.Second);

【讨论】:

    【解决方案4】:

    ... C# 7.0 在使用 显式 System.ValueTuple 数据类型。因此,如果您替换 var 在图 1 的示例 8 中,您最终会收到警告,表明每个项目 名称将被忽略。

    Source

    我发布这个答案是因为docs 最接近解释这一点的是:

    ValueTuple 类型的数据成员是字段。 Tuple 的数据成员 类型就是属性。

    【讨论】:

      【解决方案5】:

      我不相信。以下是我在 ValueTuple 上找到的文档:

      https://docs.microsoft.com/en-us/dotnet/api/system.valuetuple-7?view=netframework-4.7

      我个人没有使用过 ValueTuble 类型。我以前使用过 Tuple 类是这样的:

      var tuple = new Tuple<int, string>(21, "Custard");
      var number = tuple.Item1;
      var st = tuple.Item2;
      

      但是我发现使用元组,特别是当通过方法传递时很笨重。总是必须知道 Item1 和 Item2 等中的内容。根据 ValueTuple 文档,它的使用方式相同。

      你不应该只做一个实体类吗?

      我使用了我认为适合您需要的匿名类型。

      var anyom = new
      {
          age = 21,
          favoriteFood = "Custard"
      };
      
      number = anyom.age;
      st = anyom.favoriteFood;
      

      【讨论】:

      • 不,不是,@BanksySan。编译器在处理ValueTuple structs时有特殊的行为,但类型本身有Item1Item2等,就像Tuple类有属性一样。
      • @PauloMorgado 是的,我的错。 “魔法”在于语言,而不是 IL。
      • 我同意 Paulo Morgado 的观点:编译器不是解释器 @BanksySan,而是将代码翻译成非人类可读格式的组件,由 IL 解释器运行。否决了花花公子的回答。
      • 我想删除错误的答案不会伤害任何人。
      猜你喜欢
      • 2018-03-18
      • 1970-01-01
      • 1970-01-01
      • 2020-10-30
      • 2015-03-26
      • 2017-10-30
      • 2017-09-03
      • 1970-01-01
      • 2011-01-15
      相关资源
      最近更新 更多