LINQ如何使用泛型类型?

时间:2018-01-10 13:17:33

标签: c# linq

我有以下通用方法,我需要能够执行LINQ Where查询:

public static List<T> GetItems<T>(Guid parentId = new Guid()) where T : new()
{
    var db = new SQLiteConnection(_dbPath);
    List<T> result;

    if (parentId != Guid.Empty)
    {
        result = db.Table<T>().Where(i => i.ParentId.Equals(parentId)).ToList();
    }
    else
    {
        result = db.Table<T>().ToList();
    }

    db.Close();
    return result;
}

编译器不喜欢以下行

result = db.Table<T>().Where(i => i.ParentId.Equals(parentId)).ToList();
  

错误:无法解析'ParentId'

是否可以在LINQ查询中以这种方式使用泛型?请注意,类型为T的对象将始终具有ParentId属性。

3 个答案:

答案 0 :(得分:5)

您应该将T参数与某些包含所需值的接口相结合。此外,您应该将此接口添加到包含此类的字段或基类型的所有类型。

public interface IHierarchy
{
    public Guid ParentId { get; }
}

public static List<T> GetItems<T>(Guid parentId = new Guid()) 
    where T : IHierarchy, new()
{
    var db = new SQLiteConnection(_dbPath);
    List<T> result;

    if (parentId != Guid.Empty)
    {
        result = db.Table<T>().Where(i => i.ParentId.Equals(parentId)).ToList();
    }
    else
    {
        result = db.Table<T>().ToList();
    }

    db.Close();
    return result;
}

如果您有两种类型的实体,第一种包含所需的值而第二种不包含,则此方案可能会有两次重载。

答案 1 :(得分:2)

您使用的是通用类型,编译器不知道您将使用哪个entity

只需使用reflection语言的.NET功能。

result = db.Table<T>().Where(i => i.GetType().GetProperty("ParentId").GetValue(src, null).Equals(parentId)).ToList();

答案 2 :(得分:1)

问题在于,在您的代码中,您假设每个T都有一个GUID属性ParentId,而实际上您只需要每个T都有一个默认构造函数。您需要要求每个T都有一个ParentId。

你可以通过要求每个T实现一些接口来做到这一点。和其他答案一样,但是这非常麻烦,因为对于你想要使用这个函数的每个类,你需要实现这个接口。

函数Enumerable.Where似乎能够完成相同的工作,而不需要输入项的任何接口。所以让我们使用相同的方法:

作为输入,我们告诉使用哪个属性(在您的情况下为ParentId)以及要比较的值(在您的情况下为parentId)。

我们唯一的要求是我们必须能够将ParentId与parentId进行比较:它应该是IEquatable

public List<T> GetItems<T, TKey>(Func<T, TKey> keySelector, Tkey value)
    TKey : IEquatable<TKey>,
{
    ...
     result = db.Table<T>()
       .Where(t => keySelector(t).Equals(value))
       .ToList();
}

用法:

    Guid value = ...
    var items = GetItems<MyClass, Guid>(item => item.ParentId, value);

此功能也适用于其他类和其他属性:

int studentId = ...
var students = GetItems<Student, int>(student => student.Id, studentId);
var otherStudents = GetItems<Student, string>(student => student.Name, "John");

两面评论:   - 您使用new Guid()定义一些默认Guid。使用Guid.Empty速度更快   - 您不会创建类型为T的新项。它们已经在您的dbContext.Table中。因此,您不需要new()。   - 但是,如果你的表要求T是一个类,那么你应该要求它。请参阅表定义:

where T : class