H B*_*amy 6 c# generics type-inference
我有以下C#代码不符合我的意愿.
要求是任何实现any IEnumerable<T>的"2"东西都使用第二种打印方法,但其他任何东西都使用第一种打印方法"1".
下面是一个简单的演示.ICollection<int>,IList<int>,List<int>并且int[]都实现IEnumerable<T>,但"1"打印代替"2"
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Test
{
public class Program
{
public static void Main()
{
var parent = new Parent<Class>();
// OK: TProperty == int. Prints "1"
parent.Map(c => c.IntValue);
// OK: TProperty == int. Prints "2"
parent.Map(c => c.IEnumerableIntValue);
// Wrong: TProperty == ICollection<int>. Prints "1"
parent.Map(c => c.ICollectionIntValue);
// Wrong: TProperty == List<int>. Prints "1"
parent.Map(c => c.ListIntValue);
// Wrong: TProperty == int[]. Prints "1"
parent.Map(c => c.ArrayIntValue);
}
public class Class
{
public int IntValue { get; set; }
public IEnumerable<int> IEnumerableIntValue { get; set; }
public ICollection<int> ICollectionIntValue { get; set; }
public List<int> ListIntValue { get; set; }
public int[] ArrayIntValue { get; set; }
}
}
public class Parent<T>
{
public void Map<TProperty>(Expression<Func<T, TProperty>> expression)
{
Console.WriteLine("1");
}
public void Map<TProperty>(Expression<Func<T, IEnumerable<TProperty>>> expression)
{
Console.WriteLine("2");
}
}
}
Run Code Online (Sandbox Code Playgroud)
我已经尝试将定义更改为
public void Map<TEnumerable, TElement>(Expression<Func<T, TEnumerable>> expression) where TEnumerable : IEnumerable<TElement>
{
Console.WriteLine("2");
}
Run Code Online (Sandbox Code Playgroud)
但这需要使用显式类型参数,这是不可接受的:
parent.Map<int[], int>(c => c.ArrayIntValue);
Run Code Online (Sandbox Code Playgroud)
有没有人有关于如何在编译时在C#中实现这一点的想法?任何想法都表示赞赏.也许反对/协变代表可以工作?我曾尝试与C#编译器争吵,但无处可去.
更新我之前的答案是完全错误的,没有正确思考。
不,你不能这样做。原因是它T总是比IEnumerable<T>任何非静态类型的东西更好的匹配IEnumerable<T>,这就是泛型的工作原理;T除非有竞争性的精确匹配,否则没有比这更好的通用匹配了。
考虑以下:
void Foo<T>(T t) { }
void Foo<T>(IEquatable<T> equatable) { }
Run Code Online (Sandbox Code Playgroud)
您真的希望Foo(1)解决第二个过载问题吗?
或者Foo("hello")决定Foo<char>(IEnumerable<char>)何时适用的候选人是:
void Foo<T>(T t) { }
void Foo<T>(IEnumerable<T> enumerable) { }
Run Code Online (Sandbox Code Playgroud)
最简单的解决方案是在映射时进行显式强制转换:
parent.Map(c => c.ICollectionIntValue.AsEnumerable());
parent.Map(c => c.ListIntValue.AsEnumerable());
//etc.
Run Code Online (Sandbox Code Playgroud)
您可以dynamic按照以下方式做一些奇特的混合一些反射的事情:
public void Map<TProperty>(Expression<Func<T, TProperty>> expression)
{
var genericInterfaces = typeof(TProperty).GetInterfaces().Where(i => i.IsGenericType);
var iEnumerables = genericInterfaces.Where(i => i.GetGenericTypeDefinition().Equals(typeof(IEnumerable<>))).ToList();
if (iEnumerables.Count > 1)
throw new InvalidOperationException("Ambiguous IEnumerable<>");
var iEnumerable = iEnumerables.FirstOrDefault();
if (iEnumerable == null)
{
Console.WriteLine("1");
}
else
{
//ok, we know we have an IEnumerable of something. Let the runtime figure it out.
Expression<Func<T, IEnumerable<dynamic>>> newExpression = e => expression.Compile()(e) as IEnumerable<dynamic>;
Map(newExpression);
}
}
public void Map<TProperty>(Expression<Func<T, IEnumerable<TProperty>>> expression)
{
Console.WriteLine("2");
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
611 次 |
| 最近记录: |