如何访问List的属性<T>,其中T每次都是不同的对象



如何访问List<T>的属性,其中T每次都是另一种类型的对象,学生,产品等?问题是在编译时,C# 无法识别 T 中包含的属性。如您所见(如代码中的注释所示),我制作了foreach,之后当我尝试itemList时,对象的属性不会显示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StudentLinq {
public class Student {
public int Id {
get;
set;
}
}
public class Admin {
public string Name {
get;
set;
}
}
public class Generic {
public void Print<T>(List<T> list) {
foreach(var itemList in list) {
if (list is List<Student>) {
Console.WriteLine(itemList);
}
//How can I print the properties from the product class and student 
}
}
}
class Program {
static void Main(string[] args) {
var studentsList = new List<Student>();
studentsList.Add(new Student {
Id = 2
});
var productList = new List<Product>();
productList.Add(new Product {
Price = 40
});
var generic = new Generic();
generic.Print<Student>(studentsList);
generic.Print<Product>(productList);
Console.ReadLine();
}
}
}

型的用途

泛型背后的想法是,它们对你传递给它的所有内容都做同样的事情。例如,可以编写一个泛型函数来计算任何类型的列表,因为可以对所有列表进行计数。

int GetCount<T>(List<T> source)
{
return source.Count();
}

泛型不适用什么

如果你必须对不同的类型做不同的事情,泛型不是正确的答案。处理它的典型方法是使用不同的函数,每个函数都接受与其逻辑匹配的类型。如果需要,这些函数可以具有相同的名称(这称为方法重载)。

void Print(List<Student> list)
{
foreach(var item in list)
{
Console.WriteLine("{0}", item.Id);
}
}
void Print(List<Admin> list)
{
foreach(var item in list)
{
Console.WriteLine("{0}", item.Name);
}
}

如何使方法泛型用于多个类

如果您碰巧发现某些对象具有共同的属性,则可以为它们提供一个包含一个或多个公共属性的通用接口:

interface IHasName
{
string Name { get; }
}
class Admin : IHasName
{
public string Name { get; set; }
}
class Student: IHasName
{
public string Name { get; set; }
}

如何显示您的媒体资源

拥有通用接口后,可以编写具有类型约束的泛型方法,并且您的属性将突然可供您使用。只有类型约束保证存在的那些属性才可用;这使得泛型方法类型安全。

public void Print<T>(List<T> source) where T : IHasName
{
foreach (var item in source)
{
Console.WriteLine("{0}", item.Name);
}
}

处理问题的另一种方法

处理此类问题的另一种方法是提供通用回调。

void Print<T>(List<T> source, Func<T,string> callback)
{
foreach (var item in source)
{
Console.WriteLine("{0}", callback(item));
}
}

然后这样称呼它:

var list = new List<Student>();
Print(list, item => item.Id.ToString());
var list2 = new List<Admin>();
Print(list2, item => item.Name);

这是有效的,因为访问属性的逻辑包含在回调中,而不是泛型方法中,从而回避了问题。

如果你的对象没有任何共同点,并且你不想使用Polymorphism那么一定要使用System.Reflection

public void Print<T>(List<T> list)
{
foreach (var itemList in list)
{
var properties = typeof(T).GetProperties();
var values = properties.Select(x => x.GetValue(itemList));
Console.WriteLine(string.Join(",", values));
}
}

您不再需要将该方法定义为generic

John Wu的回答提供了一些很好的建议 - 在这种情况下,值得注意的是,你不需要使用泛型来做你想要的,只需使用非泛型接口并作为对象实例与它们进行交互:

static void Print(IEnumerable data)
{
foreach (var item in data)
{
switch (item)
{
case String str:
Console.WriteLine(str);
break;
default:
throw new NotSupportedException();
}
}
}

我建议在你的类中覆盖ToString()-Method:

public class Student
{
public int Id { get; set; }
public override string ToString()
{
return Id?.ToString();
}
}
public class Admin
{
public string Name { get; set; }
public override string ToString()
{
return Name;
} 
}
public class Generic
{
public void Print<T>(List<T> list)
{
foreach (var item in list)
{
Console.WriteLine(item); // Implicitly calls ToString()
}
}
}

如果您的泛型类列表有限,则可以使用此选项。我会推荐@Bercovici阿德里安的答案。无论如何,我会给出另一种方法。

可以使用 c#as运算符将列表转换为其非泛型成员,方法是使用if

public class Generic
{
public void Print<T>(List<T> list)
{
if(list is List<Student>)
{
var studentList = list as List<Student>;
foreach (var itemList in studentList)
{
Console.WriteLine(itemList.Id);
}
}
else if(list is List<Product>)
{
var productList = list as List<Product>;
foreach(var itemList in productList)
{
Console.WriteLine(itemList.Price);
}
}
}
}