我正在开发一个项目,需要一个方法来获取变量的名称。 (XmlNode类型)。我找到了这个解决方案 - 但我不知道它是如何工作的。任何解释都会有所帮助。谢谢!
static void Main(string[] args)
{
GetName(new { var1 });
GetName2(() => var1);
}
static string GetName<T>(T item) where T : class
{
return typeof(T).GetProperties()[0].Name;
}
static string GetName2<T>(Expression<Func<T>> expr)
{
return ((MemberExpression)expr.Body).Member.Name;
}
特别是,在调用方法时,我不明白参数为(new {var1})
和(() => var1)
的原因,<T>(T item) where T : class
的含义以及<T>(Expression<Func<T>> expr)
的含义。
我确实读过关于lambda操作和<T>
的内容,但这并没有太大帮助。
答案 0 :(得分:1)
首先,我引入了nameof
运算符,我相信c#6。它可以做你想要的。你这样使用它:
var myVariable = new Object();
var myVariableName = nameof(myVariable);
现在打开你的问题。
什么是(new {var1})
?
您在这里调用GetName
方法。该方法只需一个参数。在这种情况下传递给方法的对象使用以下代码进行实例化:new { var1 }
。此处new { var1 }
正在创建anonymous type。正在创建的对象具有名为“var1”的单个属性,其值为变量var1
。由于未提供属性名称,因此属性自动赋予与变量相同的名称。当您声明匿名类型时,您可以明确命名该属性:new { var1 = var1 }
。或者你可以给你的属性一个完全不同的名字:new { DifferentName = var1 }
(但这会导致GetName
返回错误的结果 - 见下文)。如果你明确地将这些类型定义为类,它们将分别看起来像这样:
public class MyClass<T>
{
public MyClass(T property)
{
var1 = property;
}
public var1 { get; }
}
和此:
public class MyClass<T>
{
public MyClass(T property)
{
DifferentName = property;
}
public DifferentName { get; }
}
什么是<T>(T item) where T : class
?
<T>
或GetName<T>
中的GetName2<T>
是通用类型参数(generics)。在这种情况下,它允许您延迟方法参数的类型规范,直到调用该方法。因此,如果我有一个带有此签名的方法,例如,MyMethod<T>(T item)
我可以稍后用类似MyMethod<int>(2)
的int或类似MyMethod<string>('some string')
的字符串调用它。在这里,我明确指定了<int>
和<string>
的类型。在许多情况下,当类型是不明确的时,您可以排除类型声明,C#将推断它。所以我可以执行此操作MyMethod('some string')
,C#将能够推断出类型为string
。这就是这里发生的事情:GetName(new { var1 })
。由于new { var1 }
是匿名类型,因此在调用GetName
时无法显式指定类型。但是,您仍然可以通过允许C#推断类型来使用匿名类型调用GetName
。
方法签名的where T : class
部分只是generic constraint,即对可以传递给此方法的类型设置约束。在这种情况下,约束是T
必须是class
而不是值类型。
GetName<T>
如何运作?
此函数正在使用reflection来检查传递给它的对象。以下是正在发生的事情:typeof(T)
获取传递的对象类型(记住我们传递的是匿名类型),GetProperties()
获取该类型的所有属性 - 这将为您提供一个数组PropertyInfo
,[0]
为您提供该数组中的第一个属性(在new { var1 }
传递给此方法的情况下,对象将只有一个名为'var1'的属性),最后Name
为您提供该属性的名称。
此方法正在对传递给它的对象进行假设。具体来说,传递的对象至少有一个属性,并且第一个属性的名称与我们感兴趣的名称的变量具有相同的名称。这种方法远非万无一失,它可以在运行时通过传递没有属性的对象,或者如果您未能传递符合GetName
假设的对象,则可能会返回错误的名称。
有趣的是,GetName
可以在没有这样的泛型的情况下实现:
static string GetName3(object item)
{
return item.GetType().GetProperties()[0].Name;
}
也许作者试图利用至少一小部分编译时间检查来消除整个类的对象(值类型),这些对象没有属性传递给方法。
什么是(() => var1)
?
这是expression。此特定表达式表示不带参数()
并返回对象的函数。我知道GetName2
的方法签名:
GetName2<T>(Expression<Func<T>> expr)`
请参阅expr
参数是Expression
类型的Func
(函数),它不接受任何参数并返回类型为T
的on对象。
GetName2<T>(Expression<Func<T>> expr)
如何运作?
嗯......简短且可能不是非常准确的答案是它正在返回表达式的右侧。因此,您传递() => var1
并返回var1
。我们现在就把它留在那里。
答案 1 :(得分:-1)
new {var1}
创建一个匿名对象(() => var1)
是一个lambda函数,没有参数简写:
delegate void () { return var1;}
<T>(T item) where T : class
是反思并限制泛型
一个类的参数。见DarkSquirrel42答案