【问题标题】:C# declaring a variable with the type of an expression [closed]C#用表达式的类型声明一个变量[关闭]
【发布时间】:2020-05-13 17:27:03
【问题描述】:

我看到关于这个主题有一些老问题(2011 年),只是想知道是否有任何我可能错过的改变。主题领域是自动类型推导,特别是在声明变量时。这里的目标是通过避免代码中的重复来减少打字,从而减少错误。理想情况下,解决方案应该具有零运行时开销,并且阅读代码的人易于理解。

我有一个函数

(tuple-type-decl) Function(param-list) { body }

我想声明一个变量

var list = new List<return-type-of-function>();

[添加]一个具体的例子

var list = new list<typeof Function(default,fault)>();

也就是说,我希望编译器向 List 提供类型参数以及函数返回的类型,而无需重新键入或显式命名该类型。我在这里寻找一个成语,而不是如何解决特定的用例。

当然,我不能直接在 C# 中执行此操作。我认为现代 C++ 实际上会让我基本上编写该声明,但 C# typeof 运算符仅适用于类型,不适用于表达式。我能想到的最好的(而且远非“好”)是:

var list = (from i in Enumerable.Range(0,0) select Function(bogus-parms).ToList();

这实际上确实声明了一个正确类型的列表,甚至从元组类型中提取了伪成员名称,但它很笨拙且远非清晰,并且实际上具有运行时成本,尽管很小。

有没有人在 C# 7/8 时代为这个构造想出一个像样的成语?

【问题讨论】:

  • 请分享可重复的样本。另外,tuple-type-declreturn-type-of-function 有什么关系?
  • @PavelAnikhouski 特定类型并不重要 - 插入任何元组类型和任何参数列表以及任何您想要的函数体。目标是声明一个具有基于函数返回类型的类型的变量,而不是简单地重新声明类型(可能是复杂的或匿名的)。
  • 我应该在原始帖子中更清楚地说明 - 练习的重点是声明变量而不重复类型的定义(本例中为元组)并且不创建新类对于我想从函数返回的每种杂项元组类型。这允许变量声明随着函数的发展而减少编辑 - 向元组添加一个新成员,变量声明自动正确。以此类推。

标签: c# c#-8.0


【解决方案1】:

您可以使用接受 Expression&lt;Func&lt;T&gt;&gt; 然后返回 List&lt;T&gt; 的辅助函数来完成此操作,但您仍然必须指定“虚假”参数

public static List<T> ListFor<T>(Expression<Func<T>> unused)
    => new List<T>();

然后你会像下面这样使用它:

public static (int, string, double) MyFunc(int arg1, DateTime arg2)
{ 
    ... 
} 

var list = ListFor(() => MyFunc(default, default));

您甚至可以创建一个匿名类型的列表(尽管我不确定您为什么要这样做):

var list = ListFor(() => new { s = "", i = 5 });

【讨论】:

  • 不错!不幸的是,有必要为表达式编写一组虚拟参数,但可能是不可避免的。
  • 这也有一点运行时成本。运行时中的 JITer 可能会优化掉 Expression> 实例的实例化,因为它没有被使用。如果不对此进行调查,我绝对不会在紧密循环中使用它。正如您可能猜到的那样,我的目的是在循环外使用它来累积同一循环内的函数返回的值。
【解决方案2】:

你可以为T创建一个扩展方法

public static List<T> ToList<T>(this T t) => new List<T>();

https://dotnetfiddle.net/KKCDZO

加长版: https://dotnetfiddle.net/MDunr1

【讨论】:

    【解决方案3】:

    您可以创建一个从您选择的Tuple&lt;&gt; 扩展的新类。例如,您可以声明一个类:

    public class MyThing : Tuple<int, string> {
        public MyThing(int a, string b) : base(a, b) {}
    }
    

    然后你可以像任何其他类型一样在任何地方使用这个新类MyThing

    public static void Main(string[] args) {
        var obj = Foobar("test");
        Console.WriteLine(obj);
        Console.WriteLine(obj.Item1);
        Console.WriteLine(obj.Item2);
    
        IList<MyThing> list = new List<MyThing> {
            obj
        };
        Console.WriteLine(list);
    }
    
    public static MyThing Foobar(string b) {
        return new MyThing(b.Length, b);
    }
    

    这将生成以下输出:

    (4, test)
    4
    test
    System.Collections.Generic.List`1[MyThing]
    

    【讨论】:

      【解决方案4】:

      假设你的函数被声明为

      (string name, int x, int y) Function(param-list) { body }
      

      然后你可以声明一个列表为

      List<(string name, int x, int y)> list =
          (from i in Enumerable.Range(0,0) select Function(bogus-parms).ToList();
      

      请注意,我使用的是新的 ValueTuple 类型。它从 C# 7.0 开始就存在,并允许您为元组字段命名。

      然后您可以访问这样的项目

      string name = list[i].name;
      int x = list[i].x;
      int y = list[i].y;
      

      var (name, x, y) = list[i];
      Console.WriteLine($"name = {name}, x = {x}, y = {y}");
      

      这也是新的,称为String interpolation

      当然你也可以创建自己的类型

      public readonly struct MyStruct
      {
          public MyStruct(string name, int x, int y)
          {
              Name = name;
              X = x;
              Y = y;
          }
      
          public string Name { get; }
          public int X { get; }
          public int Y { get; }
      }
      

      现在类型有了名字,它的成员有了名字。您可以将列表声明为

      List<MyStruct> list = ...;
      

      它也适用于

      var list = ...;
      

      如果右边的表达式返回List&lt;MyStruct&gt;,即如果函数的返回类型为MyStruct。请注意,第二种情况下的列表强类型为List&lt;MyStruct&gt;。您只是让 C# 编译器推断它,而不是明确声明它。这发生在编译时,对运行时没有影响。

      【讨论】:

        猜你喜欢
        • 2021-05-27
        • 2014-01-18
        • 2011-12-11
        • 2013-05-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多