2013-02-05 133 views
0

我有這個C#WinForms代碼,其中我有幾個不同的structs,所有功能以相同的方式。所以,我試圖使用模板來代替編寫單獨的函數來添加或刪除項目。如何從模板調用方法?

例如,這裏是一個struct和相應的List<>我使用存儲其objects

public struct Alias 
{ 
    public string alias; 
    public string aliasSource; 

    public static bool IsValid(...); //This function exists in all the structs 
}; 

List<Alias> aliases; 

這從外部使用,添加別名功能:

public void AddAlias(Alias iAlias) 
{ 
    AddGenericStructItem<Alias>(iAlias, aliases); 
} 

這是實際添加的功能:

private void AddGenericStructItem<T>(T genericStructItem, List<T> genericList) 
{ 
    string outputError; 
    if (T.IsValid(genericStructItem, out outputError)) //< -- Problem in the 'T' being used in the far left 
    { 
     if (genericList.Contains(genericStructItem)) 
     { 
      MessageBox.Show("ERROR 82ha5jb :: Item already exists"); 
     } 
     else 
     { 
      genericList.Add(genericStructItem); 
     } 
    } 
    else 
    { 
     MessageBox.Show(outputError); 
    } 
} 

問題發生在T.IsValid...部分。編譯器給我的T以下錯誤:

'T' is a 'type parameter', which is not valid in the given context 

有沒有解決這個辦法嗎?我所有的structs都有一個IsValid函數,它們具有相同的設置,所以如果我不在這裏使用模板,反覆編寫相同的代碼似乎很愚蠢......

+2

C#泛型與C++中的模板明顯不同,儘管語法看起來很相似。 –

+0

我感興趣的是爲什麼IsValid()是靜態的。我希望稱爲IsValid()的方法需要訪問該對象。 – itsme86

+0

是的,這是一種看待它的方式,我猜。我使用它的方式是當我需要驗證輸入本身時(基於哪個結構實例在以後創建),所以驗證需要事先發生,這就是爲什麼在這種情況下需要靜態方法。 – Ahmad

回答

0

您不能使用約束來告訴編譯器對象上存在靜態方法。如果真的需要是靜態的,你需要使用反射來調用方法:

var methodInfo = typeof(T).GetMethod("IsValid", BindingFlags.Static|BindingFlags.Public); 
if (methodInfo != null) 
{ 
    object[] parameters = new object[] { genericStructItem, null }; 
    if ((bool)methodInfo.Invoke(null, parameters)) 
    { 
     // It's valid! 
    } 
    else 
    { 
     string error = (string)parameters[1]; 
    } 
} 
+0

你能告訴我你放在'(...)'裏面嗎?這裏是我正在使用的完整行:'var isValid =(bool)methodInfo.Invoke(genericStructItem,out outputError);',但是這給了我錯誤:'最好的重載方法匹配'System.Reflection.MethodBase.Invoke (object,object [])'有一些無效參數' – Ahmad

+0

我會更新我的代碼。此外,這篇文章做了一個非常好的工作,解釋靜態方法調用通過與out參數反射:http://stackoverflow.com/questions/569249/methodinfo-invoke-with-out-parameter – itsme86

+0

不知道這種情況如何適合我。我想出了什麼,我需要代替object,object []',但是我的問題是'out'參數不起作用(就像你指向的線程中的問題一樣)。然而,該頁面上的解決方案使用C#定義的函數來使用TryParse(即'GetTryParseMethodInfo()'),我不能這樣做,因爲在我自己的函數中有'IsValid'。 – Ahmad

2

你不能那樣做。唯一的選擇是爲通用參數定義where約束爲某些接口或基類類型。但是你既不能用結構也不能用靜態成員來做到這一點。如果您改變結構的類,那麼你可以做以下操作:

public interface IValidatable 
{ 
    bool IsValid(out outputError); 
} 

public class Alias : IValidatable 
{ 
    public string alias; 
    public string aliasSource; 

    public bool IsValid(out outputError) { ... }; 
}; 

現在,您可以申請限制:

private void AddValidatableItem<T>(T item, List<T> list) 
    where T : IValidatable 
{ 
    string outputError; 
    if (!item.IsValid(out outputError)) 
    { 
     MessageBox.Show(outputError); 
     return; 
    } 

    if (list.Contains(item)) 
    { 
     MessageBox.Show("ERROR 82ha5jb :: Item already exists"); 
     return; 
    } 

    list.Add(item);  
} 

順便說一句,你可以利用C#擴展方法,使這個方法的擴展可驗證的項目清單:

public static void AddValidatableItem<T>(this List<T> list, T item) 
    where T : IValidatable 

這將允許你調用方法的列表:

aliases.AddValidatableItem(newAlias); 
+0

這對於非靜態方法很好,但不適用於靜態方法。 – itsme86

+0

@ itsme86請認真閱讀我的答案的第一段更仔細 –

0

C#泛型是從C++模板顯著不同,雖然語法類似於。

當你說

T.IsValid(genericStructItem, out outputError); 

這聽起來像你期望的編譯器來替代TAlias給你

Alias.IsValid(genericStructItem, out outputError); 

這不是泛型是如何工作的。您需要找到另一種方法來調用IsValid,例如反射或爲您的結構添加通用接口。

另外我會強烈考慮使用類而不是結構。我不知道你選擇結構的原因,但總的來說有幾個原因而不是使用結構,特別是如果他們需要變化。