简体   繁体   English

使用 Generics 列表时无法编译<t>在 C#</t>

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

I have two different classes.我有两个不同的课程。 And, those two different classes have multiple properties as well.而且,这两个不同的类也有多个属性。 Consider the following two example classes,考虑以下两个示例类,

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

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

Note: For example, I added the class like above.注意:例如,我像上面一样添加了 class。 But, in reality, the two classes should have different values with the same name.但是,实际上,这两个类应该具有相同名称的不同值。

These classes should be a member of the list like below,这些类应该是如下列表的成员,

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

So, to process these list I need a two different functions something like below,所以,要处理这些列表,我需要两个不同的函数,如下所示,

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

And, the similar function to process the Int class as well.并且,类似的 function 也可以处理 Int class。 So, I planned to use the generic.所以,我打算使用泛型。 I have written the code like below,我已经编写了如下代码,

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

But, the code does not compile.但是,代码无法编译。 I'm facing the below error.我面临以下错误。

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?)

It would be much-appreciated anyone helping on this.任何对此提供帮助的人都会非常感激。

Thank you,谢谢,

You have said "This code works for every type T" and yet you expect your Type T to have a property called Name, which many many types do not have.您已经说过“此代码适用于每种类型 T”,但您希望您的类型 T 有一个名为 Name 的属性,而许多类型都没有。 Generics do not work that way. Generics 不能那样工作。

If you want to do something with your instance of type T that requires it to have certain properties, you need to tell the compiler that T is constrained to types that have those properties.如果你想对你的类型 T 的实例做一些需要它具有某些属性的事情,你需要告诉编译器 T 被限制为具有这些属性的类型。

In your case you will need to write an interface common to both of your classes and the add that interface to your generic T definition.在您的情况下,您将需要编写一个对两个类都通用的接口,并将该接口添加到您的通用 T 定义中。

This is well explained (including a good example with code) in the Microsoft documentation here在此处的 Microsoft 文档中得到了很好的解释(包括一个很好的代码示例)

You have 2 options interface or father class .您有 2 个选项interfacefather class Bot 2 ways require where T: interfaceName or where T: fatherClassName syntax Bot 2 方式需要where T: interfaceNamewhere T: fatherClassName语法

For example with interface:以接口为例:

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

Then your generic class would be那么您的通用 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;
}

You can make it completely generic using Reflection (refer Reflection | PropertyInfo ).您可以使用 Reflection 使其完全通用(请参阅Reflection | PropertyInfo )。 This way you would be able to handle any classes coming to you.这样,您将能够处理任何给您上课的课程。 Please refer to the sample code below:请参考下面的示例代码:

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

As nvoigt mentioned, in this situation we have to use ' Interface ' concept.正如nvoigt提到的,在这种情况下我们必须使用“接口”概念。

Define your classes and interface as below:定义你的类和接口如下:

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...
}

And your method which is going to use 'Key' and 'Value' should be like this:你的方法将使用'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' in method definition means type T is ' IKeyValue ' that cause I can access the ' Key ' and ' Value ' in context (just those that are in IKeyValue interface)方法定义中的“其中 T:IKeyValue”表示类型 T 是“ IKeyValue ”,这导致我可以在上下文中访问“ Key ”和“ Value ”(仅在IKeyValue接口中的那些)

This is how you can use it:这是您可以使用它的方式:

        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 '对于命名约定,我将属性名称从“”更改为“

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM