【问题标题】:Given Array.Cast<T>(), how do I determine T via reflection?给定 Array.Cast<T>(),如何通过反射确定 T?
【发布时间】:2021-07-24 03:53:29
【问题描述】:

TL;DR - 我希望这些都可以正常工作,但是(每个 cmets)它们不会:

var c1 = new[] { FileMode.Append }.Cast<int>();
var c2 = new[] { FileMode.Append }.Select(x => (int)x);
var c3 = new[] { FileMode.Append }.Select(x => x).Cast<int>();

foreach (var x in c1 as IEnumerable)
  Console.WriteLine(x); // Append (I would expect 6 here!)

foreach (var x in c2 as IEnumerable)
  Console.WriteLine(x); // 6

foreach (var x in c3 as IEnumerable)
  Console.WriteLine(x); // 6

这是一个人为的例子;如果我没有必要,我显然不会将集合转换为IEnumerable,在这种情况下,一切都会按预期工作。但我正在开发一个库,其中包含几种方法,这些方法采用object 并返回序列化的字符串表示形式。如果它通过反射确定该对象实现了IEnumerable,它将枚举它,并且在几乎所有情况下都返回预期的结果……除了Array.Cast&lt;T&gt; 这个奇怪的情况。

我可以在这里做两件事:

  1. 告诉用户首先实现IEnumerables,例如ToList()
  2. 为每个采用IEnumerable&lt;T&gt; 的受影响方法创建一个重载。

出于不同的原因,这些都不是理想的。当Array.Cast&lt;T&gt;() 被传递时,一个采用object 的方法是否有可能以某种方式推断T

【问题讨论】:

  • 查看this answer 以了解IEnumerable.Cast&lt;T&gt; 的来源。在这个特定的用例中,Cast&lt;T&gt; 只是返回原来的数组 - 类型为 FileMode 而不是类型为 int。 (它实际上并没有强制转换任何东西,因为这个特定的枚举是作为 int 实现的。)
  • 如果您处理的是IEnumerable,那么您需要使用foreach(int x in whatever) 来获得所需的演员表。

标签: c# linq reflection


【解决方案1】:

当 Array.Cast() 被传递时,一个接受对象的方法是否有可能以某种方式推断 T?

不,在你给出的例子中没有。

你得到你所做的输出的原因是Enumerable.Cast&lt;T&gt;()方法有一个优化,当它与你要求的类型兼容时允许返回原始对象:

public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) {
    IEnumerable<TResult> typedSource = source as IEnumerable<TResult>;
    if (typedSource != null) return typedSource;
    if (source == null) throw Error.ArgumentNull("source");
    return CastIterator<TResult>(source);
}

所以在你的第一种情况下,实际上什么都没有发生。 Cast&lt;T&gt;() 方法只是返回您传递给该方法的对象,因此当您将其取回时,它曾经通过 Cast&lt;T&gt;() 的事实完全丢失了。

您的问题没有任何其他细节说明您是如何陷入这种情况的,或者它在实际意义上的重要性。但我们可以肯定地说,鉴于您发布的代码,不可能实现您所说的目标。

【讨论】:

  • 谢谢,很高兴确认我至少不会发疯。如果您好奇,这个问题的背景/背景是here。除非我引入 IEnumerable&lt;T&gt; 重载,否则我可能不得不将其关闭为“无法修复”。 :)
  • 我仍然觉得有趣的是,在我原来的示例中,c1 和 c3 的行为不同。但我证实,虽然你可以将FileMode[] 转换为IEnumerable&lt;int&gt;,但先对它执行Select(x =&gt; x),但你不能。违反直觉,但它确实解释了这一点。
  • "先在上面做 Select(x => x) 你不能" -- 那是因为,虽然你可以看看 x =&gt; x 并理解这是一个恒等投影,Select() 方法在运行时这样做的成本太高,因此它没有实用的方法来优化恒等投影,就像Cast&lt;T&gt;() 优化演员表一样。 Select()别无选择,只能总是返回一个调用投影委托的新迭代器,因此返回对象总是不是原来的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-07
  • 2014-10-16
相关资源
最近更新 更多