C#在运行时为Array.SetValue类型转换

时间:2010-12-31 20:36:08

标签: c# arrays reflection casting runtime

我正在尝试使用反射创建一个数组,并将值插入其中。我正在尝试为许多不同的类型执行此操作,因此我希望能够实现createAndFillArray函数:

String propertyName1 = "prop1";
String propertyName2 = "prop2";

Type t1 = typeof(myType).getProperty(propertyName1).PropertyType.getElementType();
Type t2 = typeof(myType).getProperty(propertyName2).PropertyType.getElementType();

double exampleA = 22.5;
int exampleB = 43;

Array arrA = createAndFillArray(t1, exampleA);
Array arrB = createAndFillArray(t2, exampleB);

private Array createAndFillArray(Type t, object val){
    Array arr = Array.CreateInstance( t, 1); //length 1 in this example only, real-world is of variable length.
    arr.SetValue( val, 0 ); //this causes the following error: "System.InvalidCastException : Object cannot be stored in an array of this type."
    return arr;
}

,A类如下:

public class A{
    public A(){}

    private double val;
    public double Value{
        get{ return val; }
        set{ this.val = value; }
    }

    public static implicit operator A(double d){
        A a = new A();
        a.Value = d;
        return a;
    }
}

和B类非常相似,但int

public class B{
    public B(){}

    private double val;
    public double Value{
        get{ return val; }
        set{ this.val = value; }
    }

    public static implicit operator B(double d){
        B b = new B();
        b.Value = d;
        return b;
    }
}

和myType类似如下:

public class myType{
    public A prop1{ get; set; }
    public B prop2{ get; set; }
}

我希望隐式运算符可以确保将double转换为A类,或者将int转换为B类,并避免错误;但显然不是这样。

以上用于自定义反序列化类,该类从自定义数据格式获取数据并填充相应的.Net对象属性。我是通过反射和运行时这样做的,所以我认为两者都是不可避免的。我的目标是C#2.0框架。

我有几十个(如果不是几百个)类似于AB的类,所以我希望找到一个改进createAndFillArray方法的解决方案而不是解决方案改变了这些类。

4 个答案:

答案 0 :(得分:3)

这是使用反射来查找转换方法的an example。因此,如果您可以在您的类型上获得该方法并进行自我转换。

复制自:http://bytes.com/topic/c-sharp/answers/903775-getting-operators-using-reflection

//EndType is the type I want to PRODUCE
//StartType is the type I am pulling data FROM
MethodInfo mi = EndType.GetMethod(
    "op_Implicit", 
    (BindingFlags.Public | BindingFlags.Static), 
    null, 
    new Type[] { StartType }, 
    new ParameterModifier[0]
);
//Now if mi is NOT null, it means there is an operator that allows for converting StartType to EndType, if it is null, there isn't one

答案 1 :(得分:2)

在编译时解析转换,在您的情况下,将查看Array.SetValue类型object的第一个参数的类型,0将转换为类型{{ 1}}(即盒装)。

你有理由需要使用反射吗?正如其他人发布的那样,你可以用泛型来做到这一点。

以下是使用泛型的反射代码的近似值

object

您可以按以下方式致电

private T[] createAndFillArray<T>(T val)
{
  T[] array = new T[1];
  array[0] = val;
  return array;      
}

根据您的评论,可能的实现可能是

Array arrA = createAndFillArray<A>(exampleA);
Array arrB = createAndFillArray<B>(exampleB);

您可以使用以下内容

private Array createAndFillArray(Type t, string property, object val)
{
  object element = Activator.CreateInstance(t);
  PropertyInfo pi = t.GetProperty(property);
  if (pi != null)
  {
    pi.SetValue(element, val, null);
  }

  Array arr = Array.CreateInstance(t, 1);
  arr.SetValue(element, 0);
  return arr;
}

其中“Value”是您要设置的目标类型中的属性的名称。

答案 2 :(得分:0)

首先,泛型会使数组创建变得微不足道:

public static T[] CreateAndFill<T>(params T[] values)
{
    var retval = new T[values.Length];
    for (int i = 0; i < values.Length; i++)
        retval[i] = values[i];
    return retval;
}

第二,你的隐式强制转换不起作用的一个原因可能是因为你用来设置数组中的值的方法实际上以extern调用结束(进入我无法分辨的库中) via Reflector),它很可能不会检查隐式转换。

答案 3 :(得分:0)

这会有用......

private Array createAndFillArray<T>(object val){
    Array arr = Array.CreateInstance(typeof(t), 1); //length 1 in this example only, real-world is of variable length.
    arr.SetValue( (T)val, 0 ); //this causes the following error: "System.InvalidCastException : Object cannot be stored in an array of this type."
    return arr;
}