【问题标题】:How to overload generic IEnumerable如何重载泛型 IEnumerable
【发布时间】:2019-07-06 00:36:36
【问题描述】:

我知道这是尝试重载单个泛型和数组泛型的常见问题。所有这些答案都在谈论它为什么以这种方式工作以及如何有更好的方法来做,但没有一个显示出来。我正在寻找有关如何完成我所追求的建议的建议,我愿意重构并以不同的方式进行

这是代表问题的代码: https://dotnetfiddle.net/sWrNj3

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;

public static class MyParser
{
    public static void Parse<T>(string name, T value)
    {
        Console.WriteLine($"{name}: single");
    }

    // Must take in IEnumerable<T>
    public static void Parse<T>(string name, IEnumerable<T> collection)
    {
        Console.WriteLine($"{name}: IEnumerable");
    }

    public static void ParseObj<T>(T data)
    {
        foreach (var prop in data.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            Parse(prop.Name, prop.GetValue(data, null));
        }
    }
}

public class Program
{
    public static void Main()
    {
        MyParser.ParseObj(new
        {
            Str = "abc", 
            Num = 543, 
            Arr = new[]{1, 2, 3}, 
            Chars = new char[]{'x', 'y', 'z'}}
        );
    }
}

结果:

Str: single
Num: single
Arr: single
Chars: single

期望:

Str: single
Num: single
Arr: IEnumerable
Chars: IEnumerable

【问题讨论】:

  • 问题是GetValue()返回一个object,所以编译器总是绑定到接受object的方法。如果你想有这种差异化,你需要在运行时决定(例如if(value is IEnumerable&lt;...&gt;)。
  • 我了解这是一个常见问题。 我不同意这是一个常见问题;事实上,我认为这是对泛型的滥用。你究竟为什么要这样做?
  • 常见的错误是认为您应该首先使您的类通用,然后在您弄清楚您将使用它的目的后对其进行专门化。这很少奏效,即使它确实奏效,也需要付出巨大的努力。首先找出你的类必须支持的一种类型,并专门为这种类型编写你的类。如果后来证明你需要你的类用于不同的类型,那么是时候开始考虑扩展它了。在您知道自己需要如何旅行之前,不要再浪费时间尝试建造一个无所不能的汽车-船-飞机-雪地摩托-弹簧杆。
  • 除了以上所有好的建议之外,我还要指出,重载解决方案无论如何都不会按照您想要的方式工作。您必须将数组大小写为IEnumerable&lt;T&gt;,以便编译器选择该泛型方法重载,但这当然不是对象的运行时类型。因此,任何基于对象实际运行时类型的解决方案都会失败。您需要在运行时显式检查可枚举项,如果这是您想要的区别。

标签: c# generics ienumerable


【解决方案1】:

我认为您在那里拥有所需的所有信息,但您不能将所有工作委托给编译器,您必须自己完成工作:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using static System.Console;

public static class MyParser
{
    public static void ParseObj<T>(T data)
    {
        foreach (var prop in data.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            if(prop.PropertyType.IsArray) WriteLine($"{prop.Name}:array");
            else if(prop.PropertyType == (typeof(string))) WriteLine($"{prop.Name}:string");
            else if(prop.PropertyType.IsValueType) WriteLine($"{prop.Name}:value type");
            else if(typeof(IEnumerable).IsAssignableFrom(prop.PropertyType)) WriteLine($"{prop.Name}:IEnumerable");
            else if(prop.PropertyType.IsEnum) WriteLine($"{prop.Name}:enum");
            else if(prop.PropertyType.IsClass) WriteLine($"{prop.Name}:class");
            else WriteLine($"{prop.Name}:something else");
        }
    }
}

public class Program
{
    public static void Main()
    {
        MyParser.ParseObj(new
        {
            Str = "abc"
            , Num = 543
            , Arr = new[]{1, 2, 3}
            , Chars = new char[]{'x', 'y', 'z'}
            , SomeList = new List<string>(){"a","b","c"}
        });
    }
}

输出:

Str:string
Num:value type
Arr:array
Chars:array
SomeList:IEnumerable

* 更新 * 将代码的演变添加到 cmets 的答案中,以防其他人发现这个问题有用:

  1. https://dotnetfiddle.net/Zkx7UJ
  2. https://dotnetfiddle.net/aeCG3z
  3. https://dotnetfiddle.net/6Z49ta
  4. https://dotnetfiddle.net/u5HAFP

【讨论】:

  • 我认为这是可以接受的,但我仍然无法推断它是一个 IEnumerable dotnetfiddle.net/aeCG3z
  • 是的,不幸的是,这就是概念验证失败的地方。我必须在 ParseCollection 上接受 IEnumerable 因为在 ParseCollection 内部我正在向第三方发送 IEnumerable&lt;T&gt; 所以这就是我需要它的原因 没有办法转换为 IEnumerable 因为它已经是 IEnumerable,但我没有认为可以将其转换为任何底层类型
  • 你仍然可以这样做,但事情开始变得更加复杂:dotnetfiddle.net/u5HAFP
  • 谢谢,能够满足我的需求
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-22
  • 1970-01-01
  • 2014-07-22
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
相关资源
最近更新 更多