【问题标题】:Is it possible to know the element type at compile time without a class implementing IEnumerable<T>?如果没有实现 IEnumerable<T> 的类,是否可以在编译时知道元素类型?
【发布时间】:2012-12-14 20:20:51
【问题描述】:
  • 总结

    忽略对象不能表示为数组的情况,是否可以定义扩展(静态)方法,如:

    public static ? ToArray<TSource>(this TSource source); 
    

    并返回一个对象的数组,如果它由任何元素序列组成?如果是,? 会是什么?

  • 解释

    我考虑过以下声明:

    public static TElement[] ToArray<TElement>(this IEnumerable<TElement> source); 
    public static ? ToArray<TSource>(this IEnumerable source); 
    

    但我不能假设一个未知的类一定实现了IEnumerable&lt;T&gt;IEnumerable。我什至无法定义?,因为它只是IEnumerable,它超出了通用定义。

    我还考虑过Array 类:

    public static Array ToArray<TSource>(this TSource source); 
    

    但这意味着在编译时元素类型是未知的。

    所以我想知道是否有可能在没有实现IEnumerable&lt;T&gt; 的类的情况下在编译时知道元素类型?

【问题讨论】:

  • 查看通用约束,如 MSDN 中所述。这可能会为您指明正确的方向。 msdn.microsoft.com/en-us/library/d5x73970(v=vs.80).aspx
  • 如果该类没有实现 IEnumerable 或 IEnumerable,那么任何人将如何找到它的“元素”?如果一个类没有实现 IEnumerable,你会如何将它描述为具有“元素”?即使一个类定义了一个[int index] 索引器,也没有预定义的方法来确定允许的索引是什么。
  • 因为我的英语很差。如果我后来发现某些东西可能会导致误解,我会回滚并尝试更正。这是您问题的答案吗?

标签: c# arrays generics types


【解决方案1】:

不幸的是,IEnumerator 早于泛型,所以它没有类型信息;只是一个 IEnumerator GetEnumerator() 使 String 不那么吸引人尝试强制执行您正在尝试完成的通用实现。

我已经很久没有做 C# 了,但我想:

public static TElement ToArray<TSource>(this IEnumerable<TElement> source);

... 应该可以:

public static TElement[] ToArray<TElement>(this IEnumerable<TElement> source);

关于扩展方法要记住的重要一点是它们只是语法糖。他们实际上并没有留下来或被注入到他们正在装饰的类型中。它们在编译时被替换为静态方法调用。您使用扩展方法的里程有所不同。

【讨论】:

  • public static ? ToArray&lt;TSource&gt;(this IEnumerable source); 的情况下,您根本无法获得比object[] 更多的特定类型,因为IEnumerable 意味着除了返回的实例实现IEnumerator 之外,对其GetEnumerator 方法没有任何限制。这并不意味着元素类型在编译时不可推断,它只是意味着在IEnumerable 时没有将额外类型信息转发到调用站点的机制。
  • 是的,消费者必须强制转换它,没有两种方法,而且你 [咳咳,编译器] 不能强制执行安全转换。将此与 Java 进行对比,后者具有泛型支持但具有类型擦除 - ? 作为泛型类型参数 is 是一个有效的类型描述符,但这只是因为在 Java 中泛型是为了在编译期间强制执行类型安全时间。强制转换的运行时性能不是问题,如果这是您所关心的。不安全强制转换的运行时类型安全,是的,这绝对是一个你必须非常密切关注的问题。
【解决方案2】:

您的具体问题是什么?如果您询问 IEnumerable 中元素的类型是否在编译时总是已知的,那么答案是“不,它并不总是已知的”。非泛型 IEnumerable 不强制任何元素类型。这就是为什么您可以在任何IEnumerable 上定义foreach 循环:

IEnumerable items = GetItems();

foreach(SomeClass item in items)
{
   ...
}

但是如果items 的元素之一无法转换为SomeClass,则会抛出InvalidCastException

顺便说一句,如果你定义这样的方法:

public static TElement[] MyToArray<TElement>(this IEnumerable<TElement> source); 

然后您可以在 .NET 2.0+ 中在 string 上调用它,因为 string 在 .NET 2.0 和 3.0 中实现 IEnumerable&lt;string&gt;,在 3.5+ 中实现 IEnumerable&lt;char&gt;。事实上,内置于 .NET 中的 .ToArray() 扩展已经可以在 string 上使用,而无需您定义任何内容。

【讨论】:

    【解决方案3】:

    编译时不知道,也不可能知道。

    例如我可以这样做:

    IEnumerable myCollection;
    string discriminator = Console.ReadLine();
    if (discriminator == "string")
    {
       myCollection = new List<string>{"One", "Two", "Three", "Four"};
    }
    else 
    {
       myCollection =  new int[]{1, 2, 3, 4};
    }
    
    //what should be the type of the elements variable
    var elements = myCollection.ToArray();
    

    基本上,根据用户输入,myCollection 将是 List&lt;string&gt;int[],这两种类型几乎没有任何共同点,除了可枚举。

    因此,为了使您的 ToArray 方法起作用,您可以引入类型安全和泛型,使用 IEnumerable&lt;T&gt; 而不是 IEnumerable,或者使用对象集合,例如 object[] 作为返回方法的类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-25
      • 2018-09-16
      • 1970-01-01
      • 2018-06-20
      • 2014-08-04
      相关资源
      最近更新 更多