【问题标题】:Create a method to work on IEnumerable of all types (type unknown at design run-time)创建一个方法来处理所有类型的 IEnumerable(在设计运行时类型未知)
【发布时间】:2016-01-22 06:10:45
【问题描述】:

我知道这很可能是一个涉及泛型的简单问题,或者很可能只是一个很大的“不可以”,但我很想知道我是否可以解决这个问题。

我正在尝试创建一个接收 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);
}

然后,我想我可以写 something 类似以下之一作为我的 Test 方法的方法签名:

1.把它写成一个 IEnumerable 对象:

private void Test(IEnumerable<object> MyEnumeration)

尝试通过以下方式调用它:

Test((IEnumerable<object>)x);

导致无法从IEnumerable&lt;int&gt; 转换为IEnumerable&lt;object&gt; 的运行时错误

2。尝试使用泛型:

private void Test<T>(IEnumerable<T> MyEnumeration)

尝试通过以下方式调用它:

Test(x);

导致签名不正确/无效参数的设计时错误。

或通过:

Test<type>(x);

导致无法找到类型或命名空间type 的设计时错误。


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

谢谢!

【问题讨论】:

  • 没有理由看签名失败。但是,“两者都不起作用”是对正在发生的事情的可怕解释,这使得这很难解决。当您尝试此操作时,哪些具体不起作用,以及 Test 方法试图完成的一般情况是什么?
  • 很公平,@TravisJ。让我更新我的问题...
  • 问题随着我的尝试而更新 - 希望这(至少有点)更有意义。

标签: c# generics


【解决方案1】:

问题在于这段代码:

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&lt;object&gt;

if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
{
    IEnumerable<object> asEnumerable = ((IEnumerable)x).Cast<object>();
    Test(asEnumerable);
}

而且,如果您想要 IEnumerable&lt;T&gt;,请参阅 Jon Skeet 的 answer 以解决不同的问题。

【讨论】:

  • @TravisJ 正确。我只是解决选项一。选项二需要 Jon Skeet:stackoverflow.com/questions/812673/…
  • 是的,实际上您至少包含了 IEnumerable 用法的选项,所以我删除了我的评论。我同意使用 Jon Skeet 建议的类型推断方法是在进行类型推断调用时遇到问题的最佳方法。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    您应该创建两个 Test() 方法的重载。一个处理IEnumerable,另一个处理IEnumerable&lt;T&gt;为什么需要方法重载? 因为IEnumerables 不是泛型IEnumerable&lt;T&gt;,并且在运行时您不会知道在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 });
     }
    

    【讨论】:

      猜你喜欢
      • 2014-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-20
      • 1970-01-01
      • 1970-01-01
      • 2020-11-19
      相关资源
      最近更新 更多