2013-02-05 93 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,例如反射或为您的结构添加通用接口。

另外我会强烈考虑使用类而不是结构。我不知道你选择结构的原因,但总的来说有几个原因而不是使用结构,特别是如果他们需要变化。