在通用参数中使用多态性
Making use of polymorphism in a generic parameter
问题
我有一个扩展方法,它扩展 IEnumerable<T>
并采用 Expression
导航到 属性,它本身必须是 IEnumerable
.
/// <summary>
/// Identify a child collection to search on
/// </summary>
/// <param name="source">source data on which to search</param>
/// <param name="property">Enumerable properties to search.</param>
public static IEnumerable<TSource> Search<TSource, TProperty>(
this IEnumerable<TSource> source,
Expression<Func<TSource, IEnumerable<TProperty>> property)
{
// Do stuff...
}
当 child 属性 定义为 IEnumerable<T>
时,上面的方法运行良好,可以按如下方式调用:
var result = shops.Search(s => s.ProductEnumerable);
然而 如果 child 属性 是 ICollection<T>
、IList<T>
或任何实现 IEnumerable<T>
另一个关键点是我希望能够在不显式定义类型的情况下使用该方法。
var result = shops.Search(s => s.ProductList);
//NOT
var result = shops.Search<Shop, Product>(s => s.ProductList);
我试过的
尝试 1
我想我可以创建一个新的泛型 (TCollection
) 并限制 TCollection
是 IEnumerable<TProperty>
.
public static IEnumerable<TSource> Search<TSource, TCollection, TProperty>(
this IEnumerable<TSource> source,
Expression<Func<TSource, TCollection>> property)
where TCollection : IEnumerable<TProperty>
这失败了,因为代码不再能够找到该方法。
尝试 2
然后我想到我可以使整个第二个参数成为通用参数并对其施加正确的约束。
public static IEnumerable<TSource> Search<TSource, TCollection, TProperty>(
this IEnumerable<TSource> source,
TCollection property)
where TCollection : Expression<Func<TSource, IEnumerable<TProperty>>
这也失败并出现以下错误:
Cannot use sealed class Expression<TDelegate>
as type parameter constraint.
有没有办法实现我想要的,或者我只需要为实现 IEnumerable 的所有接口创建重载???
提前感谢您花时间阅读这篇冗长的描述
嗯,不,您显示的代码工作正常。问题一定在于您如何在搜索方法中使用 IEnumerable<TProperty>
- 请记住,因为它是 Func
中的 return 值,它 必须 是协变的。
实际可编译的代码:
void Main()
{
var enumerable = default(IEnumerable<MyItem>);
Search(enumerable, i => i.MyEnumerable);
Search(enumerable, i => i.MyCollection);
}
public static IEnumerable<TSource> Search<TSource, TProperty>
(
IEnumerable<TSource> source,
Expression<Func<TSource, IEnumerable<TProperty>>> property)
{
return null;
}
public class MyItem
{
public IEnumerable<string> MyEnumerable { get; set; }
public ICollection<string> MyCollection { get; set; }
}
编译或运行时没有错误。
问题
我有一个扩展方法,它扩展 IEnumerable<T>
并采用 Expression
导航到 属性,它本身必须是 IEnumerable
.
/// <summary>
/// Identify a child collection to search on
/// </summary>
/// <param name="source">source data on which to search</param>
/// <param name="property">Enumerable properties to search.</param>
public static IEnumerable<TSource> Search<TSource, TProperty>(
this IEnumerable<TSource> source,
Expression<Func<TSource, IEnumerable<TProperty>> property)
{
// Do stuff...
}
当 child 属性 定义为 IEnumerable<T>
时,上面的方法运行良好,可以按如下方式调用:
var result = shops.Search(s => s.ProductEnumerable);
然而 如果 child 属性 是 ICollection<T>
、IList<T>
或任何实现 IEnumerable<T>
另一个关键点是我希望能够在不显式定义类型的情况下使用该方法。
var result = shops.Search(s => s.ProductList);
//NOT
var result = shops.Search<Shop, Product>(s => s.ProductList);
我试过的
尝试 1
我想我可以创建一个新的泛型 (TCollection
) 并限制 TCollection
是 IEnumerable<TProperty>
.
public static IEnumerable<TSource> Search<TSource, TCollection, TProperty>(
this IEnumerable<TSource> source,
Expression<Func<TSource, TCollection>> property)
where TCollection : IEnumerable<TProperty>
这失败了,因为代码不再能够找到该方法。
尝试 2
然后我想到我可以使整个第二个参数成为通用参数并对其施加正确的约束。
public static IEnumerable<TSource> Search<TSource, TCollection, TProperty>(
this IEnumerable<TSource> source,
TCollection property)
where TCollection : Expression<Func<TSource, IEnumerable<TProperty>>
这也失败并出现以下错误:
Cannot use sealed class
Expression<TDelegate>
as type parameter constraint.
有没有办法实现我想要的,或者我只需要为实现 IEnumerable 的所有接口创建重载???
提前感谢您花时间阅读这篇冗长的描述
嗯,不,您显示的代码工作正常。问题一定在于您如何在搜索方法中使用 IEnumerable<TProperty>
- 请记住,因为它是 Func
中的 return 值,它 必须 是协变的。
实际可编译的代码:
void Main()
{
var enumerable = default(IEnumerable<MyItem>);
Search(enumerable, i => i.MyEnumerable);
Search(enumerable, i => i.MyCollection);
}
public static IEnumerable<TSource> Search<TSource, TProperty>
(
IEnumerable<TSource> source,
Expression<Func<TSource, IEnumerable<TProperty>>> property)
{
return null;
}
public class MyItem
{
public IEnumerable<string> MyEnumerable { get; set; }
public ICollection<string> MyCollection { get; set; }
}
编译或运行时没有错误。