Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Type Casting at Runtime for Array.SetValue

I'm trying to create an array using reflection, and insert values into it. I'm trying to do this for many different types so would like a createAndFillArray function capable of this :

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

with the class A being as follows:

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

and class B being very similar, but with 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;
    }
}

and myType is something like the following:

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

I hoped that the implicit operator would have ensured that the double be converted to class A, or the int to class B, and the error avoided; but this is obviously not so.

The above is used in a custom deserialization class, which takes data from a custom data format and fills in the corresponding .Net object properties. I'm doing this via reflection and at runtime, so I think both are unavoidable. I'm targeting the C# 2.0 framework.

I've dozens, if not hundreds, of classes similar to A and B, so would prefer to find a solution which improved on the createAndFillArray method rather than a solution which altered these classes.

like image 439
Iain Sproat Avatar asked Dec 31 '10 20:12

Iain Sproat


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

Is C language easy?

Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.


2 Answers

Here's an example of using reflection to find the conversion method. So if you can get that method on your type and do the conversion your self.

Copied from: 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
like image 95
shf301 Avatar answered Sep 28 '22 06:09

shf301


Casting is resolved at compile time, which in your case would be looking at at the type of the first parameter to Array.SetValue which is of type object, the 0 will be cast to type object (ie. boxed).

Is there a reason that you need to use reflection? As others have posted you can do this with generics.

Here is an approximation of your reflection code using generics

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

Which you can call as follows

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

Based on your comment one possible implementation might be

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

Which you can use as follows

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

Where "Value" is the name of the property in the target type that you want to set.

like image 45
Chris Taylor Avatar answered Sep 28 '22 05:09

Chris Taylor