.NET会从“命名匿名”类型中受益?
-
19-08-2019 - |
题
考虑一下:
var me = new { FirstName = "John", LastName = "Smith" };
这很好,因为我们可以做到这一点:
Console.WriteLine("{0} {1}", me.FirstName, me.LastName);
但是我们不能这样做:
public T GetMe()
{
return new { FirstName = "John", LastName = "Smith" };
}
因为我们不知道T的类型。
我们可以做到这一点:
public object GetMe()
{
return new { FirstName = "John", LastName = "Smith" };
}
但是随后我们必须使用反射检查对象的属性才能访问它们:
var p = new Prog();
object o = p.GetMe();
Type t = o.GetType();
foreach (var prop in t.GetProperties())
{
Console.WriteLine(prop.Name + ": " + prop.GetValue(o, null));
}
但是,如果我们可以在定义的情况下命名匿名类型呢?当然,它不再是匿名的,但是比正常的班级定义更为简洁和可维护。
考虑一下:
public Person GetMe()
{
return new public class Person { FirstName = "John", LastName = "Smith" };
}
好处是,将有可能从方法中返回复杂的LINQ查询结果,而无需明确定义类。
考虑此相对复杂的LINQ查询:
List<int> list = new List<int>();
var query = from number in list
select
new
{
Number = number,
Square = number*number,
Absolute = Math.Abs(number),
Range = Enumerable.Range(0, number)
};
而不是定义这样的课程:
public class MyNumbers
{
public int Number { get; set; }
public int Square { get; set; }
public int Absolute { get; set; }
public IEnumerable<int> Range { get; set; }
}
为了从方法返回查询变量,我们可以做到这一点:
List<int> list = new List<int>();
return from number in list
select new public class MyNumbers
{
Number = number,
Square = number*number,
Absolute = Math.Abs(number),
Range = Enumerable.Range(0, number)
};
解决方案
实际上,有一个“黑客”可以从方法中获得匿名类型。考虑一下:
public object MyMethod()
{
var myNewObject = new
{
stringProperty = "Hello, World!",
intProperty = 1337,
boolProperty = false
};
return myNewObject;
}
public T Cast<T>(object obj, T type)
{
return (T)obj;
}
您现在可以这样做:
var obj = MyMethod();
var myNewObj = Cast(obj, new { stringProperty = "", intProperty = 0, boolProperty = false });
MyNewOBJ现在将成为与匿名类型相同类型的对象。
其他提示
您需要的语言功能是:
public var GetMe()
{
return new { FirstName = "John", LastName = "Smith" };
}
那是, var
作为方法返回类型将有效,并且编译器将从返回的任何内容中推断出实际类型。然后,您必须在呼叫网站上执行此操作:
var me = GetMe();
具有同一类型成员的任何两种匿名类型都是相同的类型,因此,如果您编写了其他函数以返回相同的模式,则它们将具有相同的类型。对于任何类型的A和B,B具有A的子集的A和A的子集,则A与B分配兼容B(B就像A的基类)。如果您写信:
public var GetMeFrom(var names)
{
return new { FirstName = names["First"], LastName = names["Last"] };
}
编译器将有效地将其定义为具有两个类型参数的通用方法, T1
是名称的类型和 T2
是索引器返回的类型 T1
接受一个字符串。 T1
将受到约束,以便它必须具有接受字符串的索引器。在呼叫网站上,您只会传递任何接受字符串并返回的索引器的东西 您喜欢的任何类型, ,这将决定 FirstName
和 LastName
在返回的类型中 GetMeFrom
.
因此,类型推理将为您弄清楚所有这些,从而自动捕获可以从代码中发现的任何类型约束。
恕我直言,根问题与匿名类型无关,但是声明类太冗长。
选项1:
如果您可以声明这样的课程:
public class MyClass
{ properties={ int Number, int Square, int Absolute, IEnumerable<int> Range } }
或其他一些类似的快速方法(例如元素示例),那么您就不需要使用匿名类型进行骇人听闻的事情来保存某些代码。
当“编译器作为服务”到达C#5时,希望他们能在整合它方面做得很好,我们将能够使用元编程来清洁解决这类问题。像1958年一样的聚会!
选项2:
另外,在C#4中,您可以将匿名类型传递给 dynamic
并避免所有铸造。当然,如果您重命名一个变量,这将为您打开运行时错误,等等。
选项3:
如果C#以与C ++相同的方式实现通用物,那么您可以将匿名类型传递到方法中,只要它具有正确的成员,它就会进行编译。您将获得静态类型安全的所有好处,而没有弊端。每次我必须输入 where T : ISomething
在C#中,我很生气他们没有这样做!
您所描述的内容(命名为匿名类型)基本上是“元组类型”。
我认为它们将是C#的不错的补充。
如果我为C#设计了这样的功能,我将使用这样的语法公开它:
tuple<int x, int y>
这样你就可以做:
public tuple<int x, int y> GetStuff()
{
}
然后,我会更改匿名类型的定义,以便:
new { x = 2, y = 2}
有 tuple<int x, int y>
因为它是类型,而不是匿名类型。
使其与当前的CLR一起使用有些棘手,因为一旦您可以在公共签名中命名匿名类型,就需要能够在单独编译的组件中统一它们。可以通过将“模块构造函数”嵌入任何使用元组类型的组件中来完成。看 这个帖子 例如。
该方法的唯一缺点是,它不尊重CLR的类型生成“懒惰”模型。这意味着使用许多不同元组类型的组件可能会遇到稍微较慢的负载类型。一种更好的方法是将元组类型的支持直接添加到CLR中。
但是,除了更改CLR外,我认为模块构造函数方法是执行此类操作的最佳方法。
我会喜欢这个功能,有很多次我想要这个功能。
一个很好的例子是处理XML。您可以解析它们获得一个对象,但是您需要制作对象的具体版本以发送回呼叫者。很多时候,您会得到XML的变化很大,并且需要您进行许多课程来处理它。如果您只能使用linqtoxml作为VAR构建对象,那就不好了,然后返回呢?
我认为这将是一个很好的编译器魔术:
创建一个元组:
(int, string, Person) tuple = (8, "hello", new Person());
相当于:
Tuple<int,string,Person> tuple = new Tuple<int,string,Person>(8 ,"hello", new Person());
在功能中:
public (int, string, Person) GetTuple(){
return ...
}
获取值:
int number = tuple[1];
string text = tuple[2];
Person person = tuple[3];
您能否创建一个使用属性名称和姓氏的接口并使用它?