在 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 个选项 interface
或 father class
。 Bot 2 方式需要 where T: interfaceName
或 where 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'
我有两个不同的 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 个选项 interface
或 father class
。 Bot 2 方式需要 where T: interfaceName
或 where 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'