如何从模板中调用方法?

时间:2013-02-05 16:45:26

标签: c# templates generics data-structures struct

我有这个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函数都有相同的设置,所以重复编写相同的代码似乎很愚蠢,以防我在这里不使用模板......

3 个答案:

答案 0 :(得分: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);

答案 1 :(得分: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];
    }
}

答案 2 :(得分:0)

C#generics 显着与C ++中的模板不同,尽管语法看起来很相似。

当你说

T.IsValid(genericStructItem, out outputError);

听起来你希望编译器用T替换Alias给你

Alias.IsValid(genericStructItem, out outputError);

这不是泛型如何工作。您需要找到另一种方法来调用IsValid,例如反射或为结构添加公共接口。

此外,我强烈考虑使用类而不是结构。我不知道你选择结构的原因,但一般来说有几个原因使用结构,特别是如果它们需要是可变的。