基于泛型类型构建动态谓词

时间:2017-02-14 12:18:04

标签: c# linq generics

如何根据参数?

中传递的泛型类型使此表达式动态化

以简化形式:

public static class CompareService
{

    public static List<T> Run<T>(List<T> database_list, string directory_path)
    {          
            var csv_list = CompareService.MergeRecordsFromFiles<T>(directory);
            return CompareService.RunComparison<T>(database_list, csv_list);
    }

    public static T CompareData<T>(List<T> database_list, List<T> csv_list)
    {           
            var diff = new List<T>();

            foreach (var db_item in database_list)
            {
                // ...
                // if T is of type Deathstar compare reference_number property
                // if T is of type Stormtrooper compare id property
                // if T is of type Sith compare id and anger_level property                
                var csv_item = csv_list.FirstOrDefault(x => x.reference_number == db_item.reference_number);
                // Comparison code
                ComparisonResult result = compareLogic.Compare(db_item, csv_item);
                // ...
            }
            return diff;
    }    
}

从另一个通用服务调用它:

public static void Whatever<T>(List<T> list)
{
    // ...
    var directory_path = "C:\";
    var delta = CompareService.CompareData<T>(list, directory_path);
    // ...
}

3 个答案:

答案 0 :(得分:2)

最天真的实施方法是检查您的itemToFind是否可以投放到DeathStarStormTrooperSith,如果是,请调用instances属性。

var deathStar  = itemToFind as DeathStar;
if(deathStar != null)
    return database_list.Where(x => ((DeathStar)x).reference_number == deathStar.reference_number).FirstOrDefault();
else
{
    var sith = itemToFind as Sith;
    if(sith != null)
        return database_list.Where(x => ((Sith)x).anger_level == sith.anger_level).FirstOrDefault();
    else
        return database_list.Where(x => ((StormTrooper)x).id== ((StormTrooper)item).id).FirstOrDefault();
}

这很麻烦,包括很多演员阵容。特别是它完全绕过了使用任意类型的泛型的实际好处(如果存在则满足约束)。在你的情况下,你会有一个通用的方法,只会为三个不错的类型提供支持。

更好的方法是让所有类实现定义属性的公共接口,例如:

interface IObject {
    int Level { get; }
}

现在所有类都定义了level - property:

clas DeathStar : IObject
{
    public int Level { get { return this.reference_number; } }
}
clas Sith : IObject
{
    public int Level { get { return this.anger_level; } }
}
clas StormTrooper: IObject
{
    public int Level { get { return this.id; } }
}

您可以在类型T上使用约束来实现该接口:

public static T CompareData<T>(List<T> list, T itemToFind) where T: IObject

答案 1 :(得分:0)

为什么不喜欢这样:

public static T CompareData<T>(List<T> list, Func<T, bool> predicate)
{
    return database_list.FirstOrDefault(predicate);
} 

然后像这样使用它:

var itemToFind = new ItemToFind();
var myObjectList = new List<MyObject>();
var item = CompareData<MyObject>(myObjectList, x=> x.MyObjectProperty == itemToFind.Id);

答案 2 :(得分:0)

您可以添加属性选择器:

public static class CompareService
{
    public static T CompareData<T>(this List<T> list, T itemToFind, Func<T, int> propSelector)
    {
        int propToFind = propSelector(itemToFind); // cache
        return database_list.FirstOrDefault(x => propSelector(x) == propToFind);
    } 
}

并称之为:

listOfDeathstars.CompareData(deathStarToFind, ds => ds.reference_number);
listOfStormtroopers.CompareData(trooperToFind, t => t.id);
listOfSiths.CompareData(sithStarToFind, sith => new { sith.id, sith.anger_level});

注意:我在签名中添加了this关键字,使其成为扩展名(不确定是否有意,但忘记了关键字)。 Where(predicate).FirstOrDefault()可以缩减为FirstOrDefault(predicate)