0

I have a fieldInfo of the type TileType[][]. How could I set a value to this fieldInfo?

internal static class Program
{
    private struct Data
    {
        public enum TileType : byte
        {
            Passable,
            Unpassable
        }
        public TileType[][] TileTypes;
    }
    private static T LoadData<T>(string[] values)
    {
        var t = (T)Activator.CreateInstance(typeof(T));
        object boxed = t;
        FieldInfo field = boxed.GetType().GetField("TileTypes"); // I get TileType[][] field here.

        IList<IList<string>> tilesMatrix = new string[values.Length][];

        for (byte x = 0; x < values.Length; x++)
        {
            tilesMatrix[x] = new string[values.Length];
            tilesMatrix[x] = values;
        }

        SetValuesToJaggedArray<object, byte[], byte>(boxed, field, tilesMatrix);
        return (T)boxed;
    }
    private static void SetValuesToJaggedArray<T, TTarget, TTargetElement>(T obj, FieldInfo field, IList<IList<string>> values)
    {
        var objs = new Array[values.Count];

        for (ushort x = 0; x < values.Count; x++)
        {
            var ar = new TTarget[values.Count];

            var arobjs = new object[values.Count];

            for (ushort y = 0; y < values.Count; y++)
            {
                var elements = new List<TTargetElement>();
                for (ushort z = 0; z < values[y].Count; z++)
                {
                    var element = (TTargetElement)Convert.ChangeType(values[y][z], typeof(TTargetElement), CultureInfo.InvariantCulture);
                    elements.Add(element);
                }
                object bElts = elements.ToArray();
                var value = (TTarget)bElts;
                arobjs[y] = value;
            }

            Array.Copy(arobjs, ar, values.Count);
            objs[x] = new TTarget[values.Count];
            Array.Copy(ar, objs[x], values.Count);
        }
        object b = objs;
        field.SetValue(obj, b); // Exception here!
    }
    static void Main(string[] args)
    {
        Data d = LoadData<Data>(new string[4] { "0", "1", "2", "3" });
    }
}

I got exception from the line field.SetValue(obj, b); because I don't have a clue how to set a value there. Any help appreciated.

Fixed solution, thx to Mitch for answer:

internal static class Program
{
    public struct Data
    {
        public enum TileType : byte
        {
            Passable,
            Unpassable,
            Water,
            Empty
        }
        public TileType[][] TileTypes;
    }
    private static T LoadData<T>(string[][] values)
    {
        var t = (T)Activator.CreateInstance(typeof(T));
        object boxed = t;
        FieldInfo field = boxed.GetType().GetField("TileTypes"); // I get TileType[][] field here.
        SetValuesToJaggedArray<object, byte>(boxed, field, values);
        return (T)boxed;
    }
    private static void SetValuesToJaggedArray<T, TTargetElement>(T obj, FieldInfo field, string[][] values)
    {
        var objs = new TTargetElement[values.Length][];
        for (ushort x = 0; x < values.Length; x++)
        {
            var elements = new TTargetElement[values.Length];
            for (ushort y = 0; y < values.Length; y++)
            {
                var element = (TTargetElement)Convert.ChangeType(values[x][y], typeof(TTargetElement), CultureInfo.InvariantCulture); 
                elements[y] = element;
            }
            objs[x] = elements;
        }
        field.SetValue(obj, objs);
    }
    static void Main()
    {
        var matrix = new string[4][];
        matrix[0] = new[] { "0", "1", "2", "3" };
        matrix[1] = new[] { "1", "2", "3", "0" };
        matrix[2] = new[] { "2", "3", "0", "1" };
        matrix[3] = new[] { "3", "0", "1", "2" };
        var d = LoadData<Data>(matrix);
        const string whiteSpace = " ";
        for (byte x = 0; x < d.TileTypes.Length; x++)
        {
            for (byte y = 0; y < d.TileTypes.Length; y++)
                Console.Write(d.TileTypes[x][y] + whiteSpace);
            Console.WriteLine();
        }
        Console.ReadKey();
    }
}

output

Wallstrider
  • 856
  • 1
  • 7
  • 22
  • What is the exception? What does it say in its message? Why are you using reflection and generics? Why is `Data` defined as a `struct`? – Jean Hominal Oct 24 '14 at 08:28
  • @JeanHominal Exception is: cannot convert Array[] to TileType[][]. I use reflection and generics cuz I deserialize data from text files. 'Data' is defined as a 'struct' because I don't actually need a reference type as an output type which I will use further. I will get data from that struct only once and then delete it from the memory. – Wallstrider Oct 24 '14 at 08:50
  • You *do not* need reflection and generics to deserialize. Your usage of a `struct` is insufficiently justified, in C# a `struct` is only used when there is a proven benefit to it. Using `ushort` and `byte` as array indices is non-idiomatic and futile, as these values will be converted to `int` by the `[]` operator. You are affecting the same `values` object in each line of your matrix - were you expecting `tilesMatrix[y] = values` to do a copy of the object? What result do you expect from values `"2"` and `"3"` once converted to `TileType`? I have the feeling that you usually write C++. – Jean Hominal Oct 24 '14 at 09:27
  • Also, I do not understand why you return a `Data` structure when you could return the `TileType[][]` directly. – Jean Hominal Oct 24 '14 at 09:28
  • @JeanHominal 'Your usage of a struct is insufficiently justified, in C# a struct is only used when there is a proven benefit to it.' Example please :). 'tilesMatrix[y] = values' these are not an actual values for a matrix. for with ushort works faster than with int on my machine btw. – Wallstrider Oct 24 '14 at 09:52
  • For the choice between `class` and `struct`, please see [Choosing between Class and Struct](http://msdn.microsoft.com/en-us/library/ms229017.aspx) on MSDN. You will see that the *default* choice is to use a `class`. And as I said, why not return the `TileType[][]` directly? And please give a real example of input values, and expected output `TileType[][]`. – Jean Hominal Oct 24 '14 at 10:04
  • @JeanHominal because the Data structure actually contains more fields than 'TileType[][]' only. I don't use real example here. And i need generics because types can be random. – Wallstrider Oct 24 '14 at 10:04
  • @JeanHominal I have pretty huge matrix with '1' '0' values do want really see it? – Wallstrider Oct 24 '14 at 10:10
  • No, I guess I am fine. I am just puzzled as to why in the example, you gave something that was not a matrix, and that had strings other than `"0"` and `"1"`. – Jean Hominal Oct 24 '14 at 10:11
  • @JeanHominal The problem in conversion of types, what I wrote in example doesn't even matter. – Wallstrider Oct 24 '14 at 10:22
  • It *does* matter, because I need to understand what the intent of your code is, because you are at a dead end. – Jean Hominal Oct 24 '14 at 10:39
  • @JeanHominal Ok. Exception still not solved ;( – Wallstrider Oct 24 '14 at 11:07

1 Answers1

1

If you cut through the rest of your example, you are left with the following:

struct Data
{
    byte[][] TitleTypes;
}

static void Main(string[] args) 
{
    var d = new Data();
    var field = typeof(Data).GetField("TitleTypes");

    var newValue = new byte[5];
    // This line fails because you are setting a field of type byte[][] with 
    // a value of byte[]
    field.SetValue(d, newValue);
}

Either set a specific index into the array or set TitleTypes with a value of the appropriately jagged array. See Setting value in an array via reflection for details on why and how.

static void Main(string[] args) 
{
    var d = new Data();
    var field = typeof(Data).GetField("TitleTypes");

    // set to index 0
    var newValue = new byte[5];
    var arrTarget = (byte[][])field.GetValue(d);
    arrTarget[0] = newValue;

    // replace entire array
    var newArray = new byte[5][];
    field.SetValue(d, newArray);
}
Community
  • 1
  • 1
Mitch
  • 21,223
  • 6
  • 63
  • 86