在运行时创建一个通用数组

7
在C#中是否有一种类似以下方法的方式:
public void Foo<T>()
{
    T[] arr = Goo() as T[];
}

Goo返回一个object[],使用反射或其他方法?


我认为一个 object[] 永远不可能是一个 T[]。但是一个 object 可以是。 - Damien_The_Unbeliever
@Ani - 我可能读错了,但似乎它表明 object[] a = new T[] 将是有效的(where T:class),而不是反过来。 - Damien_The_Unbeliever
1
@Damien_The_Unbeliever 这取决于 object[] 实际上是什么 ;p 由于 T[] 可以表示为 object[],因此您的 object[] 可能实际上是 T[]。但是,如果数组是作为 object[] 构造的,但恰好包含所有 T,那么确实:转换将失败。 - Marc Gravell
2个回答

7

您可以使用LINQ:

public void Foo<T>()
{
    T[] arr = Goo().OfType<T>().ToArray();
}

在你的示例中,你将 object[] 强制转换为 T[],如果这两种类型完全匹配,也会起作用:
public void Foo<T>()
{
    T[] arr = Goo() as T[];
    if (arr != null)
    {
        // use the array
    }
}

例如,以下情况将起作用:
public object[] Goo()
{
    return new string[] { "a", "b" };
}

然后像这样调用Foo:

Foo<string>();

1
也许最简单和最可靠的方法是复制数组,逐个进行强制转换:
public void Foo<T>()
{
    T[] arr = Array.ConvertAll(Goo(), x => (T)x);
}

这是一个 "unbox.any",这意味着(对于JIT):
  • 如果T是引用类型,则进行 castclass (即保留引用的类型检查)
  • 如果T是值类型,则进行unbox

引用类型数组是协变的,这意味着一个 T[] 可以被表示为一个 object[]。因此,Goo() 的结果可能实际上是一个 T[]。我们可能也想要测试这一点:

public T[] Foo<T>()
{
    var tmp = Goo();
    T[] arr = tmp as T[];
    if(arr == null && tmp != null) {
        arr = Array.ConvertAll(Goo(), x => (T)x);
    }
    return arr;
}

然而,这样做的一个烦恼是我们现在有了不同的语义 - 在某些情况下它是相同的数组,在某些情况下它是复制的。如果我们改变数组,这可能会带来问题。


1
哦,你的签名中有void,但是你返回了一个数组?返回类型不应该是T[]吗? - Noctis
@Nocitus 对啊,手机编程的危险性:) - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接