【问题标题】:Convert from object[] to double[] on C#在 C# 上从 object[] 转换为 double[]
【发布时间】:2015-08-05 23:10:06
【问题描述】:

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

double[] values;

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

不幸的是,我得到一个编译器错误:

无法从 object[] 转换为 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() 创建一个type 类型的对象并为其分配一个随机值(随机整数、随机字符串、随机双精度、仅基本类型)

这是 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;
        }
    }

【问题讨论】:

  • 有什么理由不使用泛型?
  • 没有充分的理由,请您详细说明一下吗?
  • 我会将GetData 定义为:public T[] GetData&lt;T&gt;(int count); 这样您就可以从方法中得到正确的类型。
  • 我试过了,但它说:“无法将表达式类型 object[] 转换为返回类型 T[]”
  • 不,您还需要在方法内部创建一个 T 数组。由于您没有展示该方法如何根据类型实际生成随机数据,因此很难更好地解释您将如何编写它。

标签: c# arrays dynamic casting


【解决方案1】:

使用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]

请注意,如果其中一项无法转换为 double,您可能会收到 InvalidCastException

【讨论】:

    【解决方案2】:

    你可以:

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

    【讨论】:

    • 这将抛出'System.InvalidCastException'
    • 仅当数组中的对象实际上不是双精度数时。但我们知道他们是因为我们要求双打。
    • @HosseinNarimaniRad:这是一个简单的fiddle 没有InvalidCastException
    • @MattBurland 是的,我的示例数据有问题
    【解决方案3】:

    如果 factory.GetData 返回一个 double 对象数组,可以使用:

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

    【讨论】:

    • 这会抛出'System.InvalidCastException'
    • @HosseinNarimaniRad 仅当 GetData 确实 not 返回一个包装双精度数组。在这种情况下,GetData 和问题中都存在错误
    【解决方案4】:

    现在所有答案(大部分)实际上都回答了这个问题,没有一个真正谈论使用泛型代替。现在这可能不适合您的直接账单,但可以很容易地添加以解决问题,并且不需要调用应用程序了解如何理解返回值。

    这很简单。只需定义一个接受泛型类型的重载(注意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的类型。

    最终编辑

    如 cmets 中所述,您可以将 object GetRandom(Type type) 更改为 T GetRandom&lt;T&gt;()。这将允许您为随机生成特定类型。我建议阅读泛型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&lt;T&gt;() 方法了。

    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());
    }
    

    【讨论】:

    • 我快到了,您能否解释一下“生成我们的数组”部分,它如何链接到您在下面定义的列表?
    • 好的,很好用!!!!!!如果想要一个 GetRandom() 函数,它返回一个与 GetData() 完全相同的类型 T 怎么办?因为我什至想生成单个对象而不是数组。所以我可以有一个公共的 GetRandom(),它返回一个分配有随机数据的对象
    • 我刚刚按照你说的做了,但是将 GetRandom() 声明为 T GetRandom() 换行:list.Add((T)GetRandom(tType));
    • @G_G 我刚刚完成了最后的编辑,但是是的,您可以更改 GetRandom 方法以返回键入的响应。希望这能回答问题并对您和其他人有所帮助。
    • 将行更改为 list.Add(GetRandom&lt;T&gt;()) 并从 GetRandom 方法中删除 Type 参数。有时使用泛型需要做更多的工作,但您为最终用户提供了更多的透明度。
    【解决方案5】:

    假设GetData 确实返回装箱为数组的双精度,您可以使用Cast() 将所有元素转换为双精度:

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

    或者您可以使用OfType() 过滤掉双精度值

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

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

    【讨论】:

    • 这会抛出'System.InvalidCastException'
    • 不,它不会,它会工作得很好。这是在声明对象数组仅包含双精度的问题所施加的限制范围内。
    猜你喜欢
    • 2023-01-23
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 2021-05-19
    • 1970-01-01
    • 2011-07-02
    • 1970-01-01
    • 2015-02-07
    相关资源
    最近更新 更多