创建一个方法来处理所有类型的 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


如何做到这一点,

或者这只是糟糕的编程实践,并且有更好的方法来做到这一点?

谢谢!

创建一个方法来处理所有类型的 IEnumerable(设计运行时类型未知)

问题出在这段代码上:

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>);

同样,这具有可怕的代码气味。你应该详细说明数据是如何进来的,我相信有人可以帮助你。