2017-06-22 32 views
-2

我目前正在開發一個項目,並且需要一個方法讓我獲得一個變量的名稱。 (XmlNode類型)。我找到了這個解決方案 - 但我不知道它是如何工作的。任何解釋都會有幫助。謝謝!C# - 需要幫助瞭解Lambda操作和<T>

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

對於整個問題的一部分,請閱讀[泛型](https://docs.microsoft.com/zh-cn/)。com/en-us/dotnet/csharp/programming-guide/generics /) –

+1

最好的部分是這些方法都不會給你一個變量的名字。 –

+0

@CamiloTerevinto我剛剛運行了代碼,在定義了var1後,我就得到了它。 – Fabulous

回答

-1
  • new {var1}創建匿名對象
  • (() => var1)是不帶參數lambda函數簡寫: delegate void() { return var1;}
  • <T>(T item) where T : class是反射和約束通用 參數的類。見DarkSquirrel42答案
+0

'delegate void(){return var1;}'不是創建委託或匿名方法的有效語法。另外,所討論的lambda *不是創建委託*,而是創建一個Expression對象。通用約束不使用反射。具有通用約束的方法恰好使用它內部的反射,但這是非常不同的。 – Servy

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)或像這樣的字符串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爲您提供該屬性的名稱。

此方法正在對傳遞給它的對象進行假設。具體而言,傳遞的對象至少有1個屬性,第一個屬性的名稱與我們感興趣的變量的名稱相同。該方法遠非萬無一失,它很容易在運行時通過傳遞沒有屬性的對象,或者如果您未能傳遞符合GetName正在製作的假設的對象,則可能返回錯誤的名稱。

有趣的是,GetName可能已經沒有泛型實現這樣的:

static string GetName3(object item) 
{ 
    return item.GetType().GetProperties()[0].Name; 
} 

也許作者試圖採取至少編譯時通過消除一整類對象的檢查一點點的優勢(價值類型)沒有傳遞給方法的屬性。

什麼是(() => var1)

這是一個expression。這個特定的表達式表示一個函數,它不帶參數()並返回一個對象。我知道從GetName2方法簽名:

GetName2<T>(Expression<Func<T>> expr)` 

看到,expr參數是Func類型(功能)不帶參數,並返回一個上T類型的對象的Expression

GetName2<T>(Expression<Func<T>> expr)如何工作?

那麼......短暫的,可能不是很準確的答案是,它返回表達式的右側。所以你通過() => var1,你得到var1。現在讓我們把它留在那。