在C#中将object[]转换为double[]

3

我有一个生成包含不同数据的对象的函数(该函数根据类型填充对象中的随机数据)。由于类型仅在运行时知道(并作为参数传递给函数),因此函数返回一个object[]

double[] values;

values = factory.GetData(typeof(double), 10);

很遗憾,我得到了一个编译器错误:

无法将对象[]转换为double[]。

我如何在程序中将object[]进行类型转换?

编辑:

这是原始函数:

    public object[] GetData(Type type, int howMany)
    {
        var data = new List<object>();

        for (var i = 0; i < howMany; i++)
        {
            data.Add(Convert.ChangeType(GetRandom(type), type));
        }

        return data.ToArray();
    }

GetRandom() 函数会创建一个指定类型的对象,并为其赋予随机值(随机整数、随机字符串、随机双精度浮点数,只包括基本类型)。

以下是 GetRandom() 函数的代码:

   public T GetRandom<T>()
    {
        var type = typeof(T);

        if (type == typeof(int))
        {
            return prng.Next(0, int.MaxValue);
        }

        if (type == typeof(double))
        {
            return prng.NextDouble();
        }

        if (type == typeof(string))
        {
            return GetString(MinStringLength, MaxStringLength);
        }

        if (type == typeof(DateTime))
        {
            var tmp = StartTime;
            StartTime += new TimeSpan(Interval * TimeSpan.TicksPerMillisecond);
            return tmp;
        }
    }

9
为什么你不使用泛型? - Lasse V. Karlsen
没有充分的理由,你能详细说明一下吗? - Gianluca Ghettini
2
我会将 GetData 定义为:public T[] GetData<T>(int count); 这样你的方法返回值类型就是正确的了。 - Lasse V. Karlsen
我尝试了,但它显示:“无法将表达式类型 object[] 转换为返回类型 T[]”。 - Gianluca Ghettini
2
不,你还需要在方法内部创建一个T类型的数组。由于你没有展示这个方法如何根据类型生成随机数据,因此很难更好地解释如何编写它。 - Lasse V. Karlsen
@LasseV.Karlsen 我已经更新了我的问题,并附上了函数本身。 - Gianluca Ghettini
5个回答

6
使用 Array.ConvertAll
values =  Array.ConvertAll(factory.GetData(typeof(double), 10), item => (double)item);

例子:

object[] input = new object[]{1.0, 2.0, 3.0};
double[] output = Array.ConvertAll(input, element => (double)element); // [1.0, 2.0, 3.0]

请注意,如果其中一项无法转换为双精度浮点数,则可能会出现“InvalidCastException”。

3
你可以:
values =  factory.GetData(typeof(double), 10).Cast<double>().ToArray();

这将抛出'System.InvalidCastException'异常。 - Hossein Narimani Rad
只有当数组中的对象实际上不是双精度浮点数时才会出现这种情况。但我们知道它们是双精度浮点数,因为我们要求的就是双精度浮点数。 - Matt Burland
1
@HosseinNarimaniRad:这里有一个简单的fiddle,没有InvalidCastException - Matt Burland
@MattBurland 是的,我的示例数据有问题。 - Hossein Narimani Rad

3
如果 factory.GetData 返回一个双精度对象的数组,则可以使用以下代码:
values = factory.GetData(typeof(double), 10).Cast<double>().ToArray();

这会引发“System.InvalidCastException”异常。 - Hossein Narimani Rad
只有当GetData没有返回包含双精度浮点数的数组时,才会使用@HosseinNarimaniRad。在这种情况下,GetData和问题都存在错误。 - Panagiotis Kanavos

1

假设 GetData 返回封装为数组的双精度浮点数,则可以使用 Cast() 将所有元素强制转换为双精度浮点数:

values=factory.GetData(typeof(double), 10).Cast<double>().ToArray();

或者您可以使用OfType()来过滤掉不是double类型的值

values=factory.GetData(typeof(double), 10).OfType<double>().ToArray();

更好的选择是将 GetData 重写为一个通用方法,并返回一个 T[]

这将抛出 'System.InvalidCastException' - Hossein Narimani Rad
2
不,它不会出问题,它会正常工作。这是在问题中规定对象数组仅包含双精度浮点数的限制范围内。 - Lasse V. Karlsen

1
现在所有的答案(大多数)实际上回答了问题,但没有一个真正讨论使用泛型。现在这可能不符合您的要求,但可以很容易地添加以解决问题,并且调用应用程序不需要了解返回值的方法。
这很简单。只需定义一个接受泛型类型(请注意T)的重载即可。
public T[] GetData<T>(int count)
{
    Type tType = typeof(T);
    //.. TODO: Generate our array.. anyway you wish..
    List<T> list = new List<T>();
    for (int i = 0; i < count; i++)
        list.Add(Activator.CreateInstance<T>());
    return list.ToArray();
}

所以这是一个基本的例子,可以通过以下方式调用:
Factory factory = new Factory();
var arr = factory.GetData<double>(10); //returns a typed array of double

现在从调用者的角度来看,我们知道接收到的数据是 double 类型或他们提供的类型。
这是对你最初问题的一种替代方案。但是,如果您的对象数组不总是与最初请求的类型相同,则此方法将无法使用。 编辑 定义数组的方式实际上取决于您如何定义对象,但让我们只采用您最初的概念并将其适应到上述情况:
public T[] GetData<T>(int count)
{
    Type tType = typeof(T);
    //.. TODO: Generate our array.. anyway you wish..
    List<T> list = new List<T>();
    for (int i = 0; i < count; i++)
        list.Add((T)GetRandom(tType));
    return list.ToArray();
}

在新的示例中,我们假设方法GetRandom()将返回所请求的类型。所请求的类型是基于typereference(typeparam)T的通用类型。我们可以通过调用typeof(T)来获取实际类型。现在在这个例子中,我们简单直接地强制转换GetRandom()对象响应(我假设GetRandom()返回一个object类型)。

最后编辑

如评论所述,您可以将 object GetRandom(Type type)更改为 T GetRandom<T>()。这将允许您生成特定类型的随机数。我建议您阅读有关泛型https://msdn.microsoft.com/en-us/library/512aeb7t.aspx的知识。

现在,一个不太明显的事情是你的泛型名称由你自己决定。您不必使用T,并且可以在一个方法调用中使用多个泛型,就像您可能已经使用的许多方法一样。

** 最终编辑 **

只是为了阐述如何将您的GetRandom方法更改为通用方法,我们仍然必须使用类型object进行工作,因为它实际上是唯一允许任何类型进行直接装箱转换的类型。您可以使用as关键字,但这可能会导致其他问题。现在,GetRandom(Type type)方法正在返回该类型的随机对象。正如所述,这仅限于几种类型,因此让我们组合一个示例。
首先要理解的是如何处理各种类型。现在,个人而言,我喜欢定义一个接口。因此,让我们定义一个接口,使所有我们的随机类型都继承它。如下:
interface IRandomTypeBuilder
{
    object GetNext();
}

提供一个简单的界面来使用GetNext()方法返回一个随机类型实体。这将根据泛型参数T返回一个类型化的响应。

现在介绍一些此接口的简单实现。

class DoubleRandomBuilder : IRandomTypeBuilder
{
    static Random rng = new Random();        
    public object GetNext()
    {
        return rng.NextDouble() * rng.Next(0, 1000);
    }
}

class IntRandomBuilder : IRandomTypeBuilder
{
    static Random rng = new Random();
    public object GetNext()
    {
        return rng.Next(int.MinValue, int.MaxValue);
    }
}

class StringRandomBuilder : IRandomTypeBuilder
{
    static Random rng = new Random();
    static string aplha = "abcdefghijklmnopqrstuvwxyz";
    public object GetNext()
    {
        string next = "";
        for (int i = rng.Next(4, 10), j = i + rng.Next(1, 10); i < j; i++)
            next += aplha[rng.Next(0, aplha.Length)];
        return next;
    }
}

class BoolRandomBuilder : IRandomTypeBuilder
{
    static Random rng = new Random();
    public object GetNext()
    {
        return rng.Next(0, 2) % 2 == 0;
    }
}

是的,这些很简单,但我们有4种不同类型,它们都定义了GetNext()方法,并为该类型返回一个随机值。现在我们可以定义GetRandom<T>()方法。

public T GetRandom<T>()
{
    Type tType = typeof(T);
    IRandomTypeBuilder typeGenerator = null;
    if (tType == typeof(double))
        typeGenerator = new DoubleRandomBuilder();
    else if (tType == typeof(int))
        typeGenerator = new IntRandomBuilder();
    else if (tType == typeof(string))
        typeGenerator = new StringRandomBuilder();
    else if (tType == typeof(bool))
        typeGenerator = new BoolRandomBuilder();
    return (T)(typeGenerator == null ? default(T) : typeGenerator.GetNext());
}

我差不多到了,你能再解释一下“生成我们的数组”部分吗?它如何与您在下面定义的列表相关联? - Gianluca Ghettini
好的,太棒了,它能正常工作!!!!!如果我想要一个GetRandom()函数,它可以像GetData()一样返回类型T怎么办?因为我想生成单个对象而不是数组。所以我可以有一个公共的GetRandom()函数,它返回一个带有随机数据的单个对象。 - Gianluca Ghettini
将该行更改为list.Add(GetRandom<T>()),并从GetRandom方法中移除Type参数。有时候使用泛型需要更多的工作,但是您为最终用户提供了更多透明度。 - Nico
抱歉,我在这方面缺乏经验,但我尝试(我使用GetRandom()更新了我的问题)将GetRandom()函数转换为T GetRandom<T>(),但它说我无法将int/double/DateTime/string转换为T。 - Gianluca Ghettini
@G_G。我又添加了一个编辑...希望能对某人有所帮助... ;) - Nico
显示剩余3条评论

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