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

时间:2016-01-21 21:02:17

标签: c# generics

我知道这很可能是一个涉及泛型的简单问题,或者很可能只是一个很大的“禁忌”整体,但我很想知道我是否可以实现这一点。

我正在尝试创建一个接收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。把它写成好像是IEnumerable对象:

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的设计时错误。

如何做到这一点,或者只是糟糕的编程习惯,还有更好的方法吗?

谢谢!

3 个答案:

答案 0 :(得分:2)

问题在于此代码:

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的answer以解决其他问题。

答案 1 :(得分:0)

这有一个糟糕的代码味道,所以我会详细说明你真正想做的事情,也许有人可以提供帮助。您的运行时错误是由于您拥有的集合实际上不是IEnumerable [object],而是IEnumberable [int]。你必须先打电话给.Cast。 所以这将是双重演员。

Test(((IEnumerable<int>)x).Cast<object>);

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

答案 2 :(得分:0)

您应该创建两个Test()方法的重载。一个处理IEnumerable而另一个处理IEnumerable<T>为什么需要方法重载?因为IEnumerables不是通用的IEnumerable<T>,并且在运行时您不会知道要使用的泛型类型Castobject

以下是展示您如何实现所需目标的示例:

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