在 C# 中使用泛型列表 <T> 时无法编译

Unable to compile while use the Generics List<T> in C#

我有两个不同的 classes。而且,这两个不同的 classes 也有多个属性。考虑以下两个示例 classes,

public Class1{
    public string Key;
    public string value;
}

public Class2{
    public string Key;
    public string value;
}

注:比如我上面加了class。但是,实际上,这两个 class 应该具有不同的同名值。

这些 classes 应该是如下列表的成员,

List<Class1> list1 = new List<Class1>();
List<Class2> list2 = new List<Class2>();

所以,为了处理这些列表,我需要两个不同的函数,如下所示,

private string GetStrValue(List<Class1> values)
{
    string toRet = string.Empty;
    if (values == null)
        return toRet;

    foreach (Class1 val in values)
    {
        if (!string.IsNullOrWhiteSpace(val.Key)) {
            toRet = val.value;
            break;
        }
    }

    return toRet;
}

而且,处理 Int class 的类似函数也是如此。所以,我打算使用泛型。我写了如下代码,

private string GetValue<T>(List<T> listValue)
{
    string toRet = string.Empty;
    if (listValue == null)
        return toRet;

    foreach (T iter in listValue)
    {
        if (!string.IsNullOrWhiteSpace(iter.Key)) {
            toRet = val.Name;
            break;
        }
    }

    return toRet;
}

但是,代码无法编译。我遇到以下错误。

Severity    Code    Description Project File    Line    Suppression State
Error   CS1061  'T' does not contain a definition for 'Name' and no accessible extension method 'Name' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)

非常感谢任何人对此提供帮助。

谢谢,

您说过“此代码适用于所有类型 T”,但您希望您的类型 T 有一个名为 Name 的 属性,而许多类型都没有。泛型不是这样工作的。

如果您想对类型 T 的实例执行某些要求它具有特定属性的操作,则需要告诉编译器 T 仅限于具有这些属性的类型。

在您的情况下,您需要编写一个对您的 类 通用的接口,并将该接口添加到您的通用 T 定义中。

这解释得很好(包括一个很好的代码示例)in the Microsoft documentation here

您有 2 个选项 interfacefather class。 Bot 2 方式需要 where T: interfaceNamewhere T: fatherClassName 语法

例如接口:

public interface IClass
{
    string Key { get; set; }
    string Name { get; set; }
    string value { get; set; }
}
public class Class1 : IClass
{
    public string Key { get; set; }
    public string value { get; set; }
    public string Name { get; set; }
}

public class Class2 : IClass
{
    public string Key { get; set; }
    public string value { get; set; }
    public string Name { get; set; }
}

那么您的通用 class 将是

private string GetValue<T>(List<T> listValue) where T : IClass
{
    string toRet = string.Empty;
    if (listValue == null)
        return toRet;

    foreach (T val in listValue)
    {
        if (!string.IsNullOrWhiteSpace(val.Key))
        {
            toRet = val.Name;
            break;
        }
    }

    return toRet;
}

您可以使用反射使其完全通用(参考 Reflection | PropertyInfo)。这样你就可以处理任何 类 来找你了。请参考下面的示例代码:

private string GetValue<T>(List<T> listValue)
{
    string toRet = string.Empty;
    if (listValue == null)
        return toRet;
    PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

    //Since you have mentioned all classes will have "Key" and "Value" and you need to use that only
    //To make it completely generic you can maybe get this as input to this function

    PropertyInfo keyProperty = properties.FirstOrDefault(x => x.Name.Equals("Key", StringComparison.OrdinalIgnoreCase));
    PropertyInfo valueProperty = properties.FirstOrDefault(x => x.Name.Equals("Value", StringComparison.OrdinalIgnoreCase));

    if (keyProperty == null || valueProperty == null)
        return toRet;

    foreach (T iter in listValue)
    {
        var keyData = keyProperty.GetValue(iter, null);
        if (keyData != null && !string.IsNullOrWhiteSpace(keyData.ToString()))
        {
            toRet = valueProperty.GetValue(iter, null).ToString();
            break;
        }
    }

    return toRet;
}

nvoigt所述,在这种情况下我们必须使用“接口”概念。

如下定义您的 类 和界面:

public interface IKeyValue
{
    public string Key { get; set; }
    public string Value { get; set; }
}

public class A : IKeyValue
{
    public string Key { get; set; }
    public string Value { get; set; }
    //other properties... 
}

public class B : IKeyValue
{
    public string Key { get; set; }
    public string Value { get; set; }
    //other properties...
}

而你要使用 'Key' 和 'Value' 的方法应该是这样的:

    private string GetValue<T>(List<T> listValue) where T: IKeyValue
    {
        string toRet = string.Empty;
        if (listValue == null)
            return toRet;

        foreach (T iter in listValue)
        {
            if (!string.IsNullOrWhiteSpace(iter.Key))
            {
                toRet = iter.Value;
                break;
            }
        }

        return toRet;
    }

'where T: IKeyValue' 在方法定义中意味着类型 T 是 'IKeyValue' 这导致我可以访问 'Key' 和 'Value' 在上下文中(只是那些在 IKeyValue 接口中的)

您可以这样使用它:

        List<IKeyValue> keyValues = new List<IKeyValue>
        {new A{Key="a",Value="b"}, new B{Key="x",Value="y"}};

        List<A> listA = new List<A> 
        { new A { Key = "h", Value = "b" }, new A { Key = "u", Value = "m" } };

        List<B> listB = new List<B> 
        { new B { Key = "h", Value = "b" }, new B { Key = "u", Value = "m" } };

        string resultListInterface = GetValue(keyValues); //valid
        string resultListA = GetValue(listA);             //valid
        string resultListB = GetValue(listB);             //valid

For naming convention I change property name from 'value' to 'Value'