如何从Template调用方法

本文关键字:调用 方法 Template | 更新日期: 2023-09-27 17:57:52

我有一个C#WinForms代码,其中有几个不同的structs,它们都以相同的方式工作。因此,我不再编写用于添加或删除项目的单独函数,而是尝试使用Templates。

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

如何从Template调用方法

你不能那样做。唯一的选择是将泛型参数的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);

不能使用约束来告诉编译器对象上将存在静态方法。如果它真的需要是静态的,那么您需要使用反射来调用方法:

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];
    }
}

C#泛型与C++中的模板有显著的不同,尽管语法看起来相似。

当你说

T.IsValid(genericStructItem, out outputError);

听起来您希望编译器用Alias替换T,以提供

Alias.IsValid(genericStructItem, out outputError);

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

此外,我强烈地考虑使用类而不是结构。我不知道你选择structs的原因,但总的来说,使用structs有几个原因,尤其是当它们需要可变时。