C# 声明具有表达式类型的变量
C# declaring a variable with the type of an expression
我看到有一些关于此主题的旧问题 (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 时代为这个构造想出一个像样的习惯用法?
您可以为 T
创建扩展方法
public static List<T> ToList<T>(this T t) => new List<T>();
假设您的函数声明为
(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<MyStruct>
,即如果函数的 return 类型为 MyStruct
。请注意,第二种情况中的列表被强类型化为 List<MyStruct>
。您无需明确声明它,而只是让 C# 编译器推断它。这发生在编译时,对运行时没有影响。
您可以创建一个新的 class,它从您选择的 Tuple<>
扩展而来。例如,您可以声明一个 class,例如:
public class MyThing : Tuple<int, string> {
public MyThing(int a, string b) : base(a, b) {}
}
然后你就可以像其他类型一样在任何地方使用这个新的 class 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]
您可以使用接受 Expression<Func<T>>
然后 returns 和 List<T>
的辅助函数来实现,但您仍然必须指定 "bogus" 参数
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 });
我看到有一些关于此主题的旧问题 (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 时代为这个构造想出一个像样的习惯用法?
您可以为 T
public static List<T> ToList<T>(this T t) => new List<T>();
假设您的函数声明为
(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<MyStruct>
,即如果函数的 return 类型为 MyStruct
。请注意,第二种情况中的列表被强类型化为 List<MyStruct>
。您无需明确声明它,而只是让 C# 编译器推断它。这发生在编译时,对运行时没有影响。
您可以创建一个新的 class,它从您选择的 Tuple<>
扩展而来。例如,您可以声明一个 class,例如:
public class MyThing : Tuple<int, string> {
public MyThing(int a, string b) : base(a, b) {}
}
然后你就可以像其他类型一样在任何地方使用这个新的 class 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]
您可以使用接受 Expression<Func<T>>
然后 returns 和 List<T>
的辅助函数来实现,但您仍然必须指定 "bogus" 参数
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 });