C# - 需要帮助了解Lambda操作和<t>

时间:2017-06-22 16:02:19

标签: c# lambda

我正在开发一个项目,需要一个方法来获取变量的名称。 (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>的内容,但这并没有太大帮助。

2 个答案:

答案 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答案