谓词中具有动态选择参数的通用方法
Generic method with dynamically selected parameter in predicate
我有许多不同类型的对象,我需要以相同的方式检查每个对象的几个不同属性。我想像这样在对象初始值设定项中使用此方法:
collection.GroupBy(x => x.Identifier)
.Select(group => new SomeType
{
Identifier = group.Key,
Quantity = group.Sum(i => i.Quantity),
Type = MY_METHOD_HERE(group),
Name = MY_METHOD_HERE(group)
})
其中一个属性的强类型示例:
private ItemType CheckItemTypeConsistency(IGrouping<string, Item> group)
{
if (group.Any(x => x.ItemType != group.First().ItemType))
{
throw new ArgumentException($"Item with number {group.Key} is inconsistent", nameof(Item.ItemType));
}
else
{
return group.First().ItemType;
}
}
但是我在 Item 中还有其他 属性 需要以相同的方式检查不同的类型,所以我有类似的方法但是 .ItemType
到处都变成了 .Name
return 类型是 string
.
我也有不同的对象类型需要使用它,所以在另一种方法中 Item
更改为 Vehicle
。
如何创建这样的泛型方法?
我试过这样的事情:
private TElement CheckConsistency<TKey, TElement>(IGrouping<TKey, TElement> group, (maybe something here?))
{
if (group.Any(x => x.(what here?) != group.First().(what here?)))
{
throw new ArgumentException($"Element with number {group.Key} is inconsistent");
}
else
{
return group.First();
}
}
我通过 returning 整个项目解决了 returning 值的问题,因此在调用此方法时我可以 CheckConsistency().Property
。
但我不知道如何处理 (what here?)
.
我想也许我可以把一些东西放在 (maybe something here?)
中,以某种方式代替 (what here?)
?
有什么想法吗?我不确定反射,因为根据集合大小和唯一条目的数量,此方法可以轻松调用超过 1000 次。
@编辑:
可以说这有点像合并来自两个文件的数据。例如 2 个项目数据集,其中将具有相同标识符等的项目的数量加在一起,但某些属性应该与名称相同,如果它们不同,那么就会出现问题,我需要抛出错误。
有不同的数据集,具有完全不同的属性,例如车辆,但规则相似,有些字段只是加在一起等等,有些必须相同。
使用访问器函数并泛化 属性 类型和对象类型,您有:
private TProp CheckConsistency<TClass, TProp>(IGrouping<string, TClass> group, Func<TClass, TProp> propFn) {
var firstPropValue = propFn(group.First());
if (group.Any(x => firstPropValue == null ? propFn(x) == null : !propFn(x).Equals(firstPropValue))) {
throw new ArgumentException($"Item with number {group.Key} is inconsistent");
}
else {
return firstPropValue;
}
}
你可以像这样使用:
var ans = collection.GroupBy(x => x.Identifier)
.Select(group => new SomeType {
Identifier = group.Key,
Quantity = group.Sum(i => i.Quantity),
Type = CheckConsistency(group, x => x.ItemType),
Name = CheckConsistency(group, x => x.Name)
});
如果在异常中包含正确的参数名称很重要,您可以将其传入,接受 Expression<Func<>>
并提取名称,然后将参数编译为 lambda 以使用(可能慢),或使用反射而不是 lambda 属性 访问器(也可能很慢)。
要使用反射,我建议缓存已编译的函数,这样您就不会在每次调用方法时都re-compile:
// [Expression] => [Func]
Dictionary<LambdaExpression, Delegate> propFnCache = new Dictionary<LambdaExpression, Delegate>();
private TProp CheckConsistency<TClass, TProp>(IGrouping<string, TClass> group, Expression<Func<TClass, TProp>> propExpr) {
Func<TClass,TProp> propFn;
if (propFnCache.TryGetValue(propExpr, out var propDel))
propFn = (Func<TClass, TProp>)propDel;
else {
propFn = propExpr.Compile();
propFnCache.Add(propExpr, propFn);
}
var firstPropValue = propFn(group.First());
if (group.Any(x => !propFn(x).Equals(firstPropValue))) {
throw new ArgumentException($"Item with number {group.Key} is inconsistent", ((MemberExpression)propExpr.Body).Member.Name);
}
else {
return firstPropValue;
}
}
我有许多不同类型的对象,我需要以相同的方式检查每个对象的几个不同属性。我想像这样在对象初始值设定项中使用此方法:
collection.GroupBy(x => x.Identifier)
.Select(group => new SomeType
{
Identifier = group.Key,
Quantity = group.Sum(i => i.Quantity),
Type = MY_METHOD_HERE(group),
Name = MY_METHOD_HERE(group)
})
其中一个属性的强类型示例:
private ItemType CheckItemTypeConsistency(IGrouping<string, Item> group)
{
if (group.Any(x => x.ItemType != group.First().ItemType))
{
throw new ArgumentException($"Item with number {group.Key} is inconsistent", nameof(Item.ItemType));
}
else
{
return group.First().ItemType;
}
}
但是我在 Item 中还有其他 属性 需要以相同的方式检查不同的类型,所以我有类似的方法但是 .ItemType
到处都变成了 .Name
return 类型是 string
.
我也有不同的对象类型需要使用它,所以在另一种方法中 Item
更改为 Vehicle
。
如何创建这样的泛型方法? 我试过这样的事情:
private TElement CheckConsistency<TKey, TElement>(IGrouping<TKey, TElement> group, (maybe something here?))
{
if (group.Any(x => x.(what here?) != group.First().(what here?)))
{
throw new ArgumentException($"Element with number {group.Key} is inconsistent");
}
else
{
return group.First();
}
}
我通过 returning 整个项目解决了 returning 值的问题,因此在调用此方法时我可以 CheckConsistency().Property
。
但我不知道如何处理 (what here?)
.
我想也许我可以把一些东西放在 (maybe something here?)
中,以某种方式代替 (what here?)
?
有什么想法吗?我不确定反射,因为根据集合大小和唯一条目的数量,此方法可以轻松调用超过 1000 次。
@编辑: 可以说这有点像合并来自两个文件的数据。例如 2 个项目数据集,其中将具有相同标识符等的项目的数量加在一起,但某些属性应该与名称相同,如果它们不同,那么就会出现问题,我需要抛出错误。 有不同的数据集,具有完全不同的属性,例如车辆,但规则相似,有些字段只是加在一起等等,有些必须相同。
使用访问器函数并泛化 属性 类型和对象类型,您有:
private TProp CheckConsistency<TClass, TProp>(IGrouping<string, TClass> group, Func<TClass, TProp> propFn) {
var firstPropValue = propFn(group.First());
if (group.Any(x => firstPropValue == null ? propFn(x) == null : !propFn(x).Equals(firstPropValue))) {
throw new ArgumentException($"Item with number {group.Key} is inconsistent");
}
else {
return firstPropValue;
}
}
你可以像这样使用:
var ans = collection.GroupBy(x => x.Identifier)
.Select(group => new SomeType {
Identifier = group.Key,
Quantity = group.Sum(i => i.Quantity),
Type = CheckConsistency(group, x => x.ItemType),
Name = CheckConsistency(group, x => x.Name)
});
如果在异常中包含正确的参数名称很重要,您可以将其传入,接受 Expression<Func<>>
并提取名称,然后将参数编译为 lambda 以使用(可能慢),或使用反射而不是 lambda 属性 访问器(也可能很慢)。
要使用反射,我建议缓存已编译的函数,这样您就不会在每次调用方法时都re-compile:
// [Expression] => [Func]
Dictionary<LambdaExpression, Delegate> propFnCache = new Dictionary<LambdaExpression, Delegate>();
private TProp CheckConsistency<TClass, TProp>(IGrouping<string, TClass> group, Expression<Func<TClass, TProp>> propExpr) {
Func<TClass,TProp> propFn;
if (propFnCache.TryGetValue(propExpr, out var propDel))
propFn = (Func<TClass, TProp>)propDel;
else {
propFn = propExpr.Compile();
propFnCache.Add(propExpr, propFn);
}
var firstPropValue = propFn(group.First());
if (group.Any(x => !propFn(x).Equals(firstPropValue))) {
throw new ArgumentException($"Item with number {group.Key} is inconsistent", ((MemberExpression)propExpr.Body).Member.Name);
}
else {
return firstPropValue;
}
}