Nick
Nick

Reputation: 461

Converting fields dynamically in C#

I have a few functions that convert uint16, uint32, int32 etc to network byte order. Let need to convert the fields for different types of struct dynamically to the network byte order, how could I do that?

eg, structType1, structType2, structType3

void toNetworkByte(what type to pass so that it could accept structType1,2 and 3?)
{
  //getfieldInfo for all the fields in the struct

  //if it is type uint16, call convertUINT16
  // elseif it is type uint32, convertUINT32
  // elseif it is typeof struct, call toNetworkByte again

}

Thanks.

Upvotes: 2

Views: 178

Answers (4)

Jason Goemaat
Jason Goemaat

Reputation: 29234

1) Structs are passed by value. If you change the values of fields in a method they are not updated in the original struct that you passed. You either need to return the modified struct or declare it as a ref parameter.

2) Boxing isn't great on performance, but it isn't that bad. The important thing though is that you need to use boxing for setting a value using reflection to work (at least from what I've found). FieldInfo.SetValue(object, object) takes an object as a parameter so it boxes the struct and sets the value on the boxed struct, but the original struct isn't updated.

Results:

a: StructA[S1=0x1122, I1=0x11223344]
a: StructA[S1=0x2211, I1=0x44332211]

Here's code that works:

class Test
{
    public static void Convert<T>(ref T arg)
    {
        object o = arg; // box so SetValue will work
        Type argType = arg.GetType();
        foreach (FieldInfo fi in argType.GetFields())
        {
            if (fi.FieldType == typeof(System.Int32))
                fi.SetValue(o, IPAddress.HostToNetworkOrder((int)fi.GetValue(o)));
            else if (fi.FieldType == typeof(System.Int16))
                fi.SetValue(o, IPAddress.HostToNetworkOrder((short)fi.GetValue(o)));
        }
        arg = (T)o; // unbox and set value of ref parameter
    }

    public static void RunTest()
    {
        StructA a = new StructA() { I1 = 0x11223344, S1 = 0x1122 };
        Console.WriteLine("a: {0}", a);
        Convert(ref a);
        Console.WriteLine("a: {0}", a);
    }
}

public struct StructA
{
    public short S1;
    public int I1;
    public override string ToString()
    {
        return string.Format("StructA[S1=0x{0:X4}, I1=0x{1:X8}]", S1, I1);
    }
}

public struct StructB
{
    public short S2;
    public int I2;
    public override string ToString()
    {
        return string.Format("StructB[S2=0x{0:X4}, I2=0x{1:X8}]", S2, I2);
    }
}

Upvotes: 0

Ilya Smagin
Ilya Smagin

Reputation: 6152

void toNetworkByte(object arg)
 {
    if (arg is Int32)
 {
 }
 else if (arg is Double)
 {
 }
 else if ( // is valueType 
 {
 }
}

is Always an option :)

Upvotes: 0

Adam Rackis
Adam Rackis

Reputation: 83366

Maybe I'm misreading the question, but it sounds like you want to be able to accept any one of 3 struct types, reflect over the fields therein, and do stuff to each field based on the type?

If that's the case, why not pass in just object? If you wanted to limit the method to just structs, you could (as @jeffora said above) say

void toNetworkByte<T>(T arg) where T : struct {
  //getfieldInfo for all the fields in the struct

  //if it is type uint16, call convertUINT16
  // elseif it is type uint32, convertUINT32
  // elseif it is typeof struct, call toNetworkByte again
}

Obviously people will be able to call your method with any struct, not just one of the three listed above. If limiting that is really important, then you'll need overloads for each type (a lot of code potentially), like @Cody Gray said.

Upvotes: 1

Cody Gray
Cody Gray

Reputation: 244843

This is a prime scenario for method overloading. You would simply have three different methods with the same name, but each of them would accept a parameter with a different type. The compiler will automatically figure out which one should be called, depending on the type of the argument you pass in. The process is completely transparent to the programmer, and the syntax is always the same. Example:

void toNetworkByte(structType1 arg)
{
    // do something for structType1
}

void toNetworkByte(structType2 arg)
{
    // do something different for structType2
}

void toNetworkByte(structType3 arg)
{
    // do something different for structType3
}

Otherwise, if all the types that you need to convert from share the same interface, you could use a generic method. The following example assumes all your structs implement the IMyStruct interface:

void toNetworkByte<T>(T arg) where T : IMyStruct 
{
    // place your conversion code here
}

Upvotes: 1

Related Questions