创建一个方法来处理所有类型的 IEnumerable(设计运行时类型未知)
本文关键字:类型 IEnumerable 运行时 未知 处理 一个 方法 创建 | 更新日期: 2023-09-27 18:31:26
我知道这很可能是一个涉及泛型的简单问题,或者很可能只是一个很大的"禁忌",但我很好奇我是否可以解决这个问题。
我正在尝试创建一个接受object
的方法,如果object
是可枚举类型,则将其传递给可以处理任何类型的可枚举的方法,但我陷入了困境。
我当前用于接收对象的方法的代码如下所示:
private void MyMethod()
{
Dictionary<string, object> MyVals = new Dictionary<string,object>();
... fill the MyVals dictionary ...
object x = MyVals["Val_1"];
Type type = x.GetType();
// Check if we have a series of values rather than a single value
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
Test(x);
}
然后,我想我可以编写以下内容之一作为我的Test
方法的方法签名:
1. 把它写成一个 IE无数的对象:
private void Test(IEnumerable<object> MyEnumeration)
尝试通过以下方式调用它:
Test((IEnumerable<object>)x);
导致运行时错误,无法从 IEnumerable<int>
强制转换为IEnumerable<object>
2. 尝试使用泛型:
private void Test<T>(IEnumerable<T> MyEnumeration)
尝试通过以下方式调用它:
Test(x);
导致设计时错误,即签名不正确/参数无效。
或通过以下方式:
Test<type>(x);
导致设计时错误,即找不到类型或命名空间type
。
如何做到这一点,
或者这只是糟糕的编程实践,并且有更好的方法来做到这一点?
谢谢!
问题出在这段代码上:
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
Test(x);
您现在知道x
是 IEnumerable,但是当编译器确定哪些方法签名兼容时,它仍被视为object
。
如果您这样做:
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
{
IEnumerable asEnumerable = (IEnumerable)x;
Test(asEnumerable);
}
然后它可以传递给
void Test(IEnumerable t)
{
}
或者,如果您真的想使用 IEnumerable<object>
:
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
{
IEnumerable<object> asEnumerable = ((IEnumerable)x).Cast<object>();
Test(asEnumerable);
}
而且,如果你想要一个IEnumerable<T>
,请参阅Jon Skeet对另一个问题的回答。
你应该创建两个 Test() 方法的重载。 一个处理IEnumerable
,另一个处理IEnumerable<T>
。为什么需要方法重载?因为IEnumerables
不是泛型IEnumerable<T>
,并且在运行时你不会知道要在object
上使用Cast
泛型类型的类型。
以下示例显示了如何准确实现所需的目标:
public void MyMethod()
{
// Sample object with underlying type as IEnumerable
object obj1 = new ArrayList();
// Sample object with underlying type as IEnumerable & IEnumerable<T>
object obj2 = (IList<string>)new List<string>();
if (typeof(IEnumerable).IsAssignableFrom(obj1.GetType()))
{
if (!obj1.GetType().IsGenericType)
{
// Handles case of IEnumerable
Test((IEnumerable)obj1);
}
else
{
// Handles case of IEnumerable<T>
InvokeGenericTest(obj2);
}
}
}
public void Test(IEnumerable source)
{
Console.WriteLine("Yes it was IEnumerable.");
}
public void Test<T>(IEnumerable<T> source)
{
Console.WriteLine("Yes it was IEnumerable<{0}>.", typeof(T));
// Use linq with out worries.
source.ToList().ForEach(x => Console.WriteLine(x));
}
/// <summary>
/// Invokes the generic overload of Test method.
/// </summary>
/// <param name="source"></param>
private void InvokeGenericTest(object source)
{
Type t = source.GetType();
var method = this.GetType().GetMethods().Where(x => x.IsGenericMethod && x.Name == "Test").First();
var genericMethod = method.MakeGenericMethod(t.GenericTypeArguments.First());
genericMethod.Invoke(this, new object[] { source });
}
这有一个不好的代码气味,所以我会详细说明你真正想做什么,也许有人可以提供帮助。您的运行时错误是由于您拥有的集合实际上不是IEnumerable[object]
而是IEnumerable[int]
。您必须先致电.Cast<object>
。
所以这将是一个双重铸造。
Test(((IEnumerable<int>)x).Cast<object>);
同样,这具有可怕的代码气味。你应该详细说明数据是如何进来的,我相信有人可以帮助你。