Приведение типов C # во время выполнения для Array.SetValue - PullRequest
5 голосов
/ 31 декабря 2010

Я пытаюсь создать массив с помощью отражения и вставить в него значения. Я пытаюсь сделать это для многих различных типов, поэтому хотел бы функцию 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;
}

с классом А следующим образом:

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.

У меня есть десятки, если не сотни классов, подобных A и B, поэтому я бы предпочел найти решение, которое улучшило бы метод createAndFillArray, а не решение, которое изменило бы эти классы.

Ответы [ 4 ]

3 голосов
/ 31 декабря 2010

Вот пример использования отражения для поиска метода преобразования. Так что, если вы можете использовать этот метод в своем типе и выполнить преобразование самостоятельно.

Скопировано из: 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
2 голосов
/ 31 декабря 2010

Приведение разрешается во время компиляции, который в вашем случае будет рассматривать тип первого параметра для Array.SetValue, который имеет тип object, 0 будет приведен к типу 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;
}

, которую вы можете использовать следующим образом

Array arrB = createAndFillArray(t2, "Value", exampleB);  

Где «Значение» - это имя свойства в целевом типе, который выхочу установить.

0 голосов
/ 31 декабря 2010

Будет ли это работать ...

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;
}
0 голосов
/ 31 декабря 2010

Во-первых, генерики сделают создание массива тривиальным:

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 (в какую библиотеку я не могу сообщить через Reflector ), который, скорее всего, не проверяет неявное преобразование.

...