1

My Goal is these.

  1. convert data class to dictionary.
  2. save/load that dictionary to/from txt, database, etc.
  3. convert dictionary to data class

I think I solved alomost, but one problem still remail.

the problem is that convert 2d string array to 2d it's original datatype array.

I have succeeded convert string to original datatype. like this

    public static void SetFieldValue(Object target, FieldInfo fieldInfo, string value)
    {
        string fieldType = fieldInfo.FieldType.Name;
        fieldType = fieldType.ToLower();

        switch (fieldType)
        {
            case "boolean":
                bool b;
                fieldInfo.SetValue(target, bool.TryParse(value, out b) ? b : false);
                break;

            case "int32":
                int n;
                fieldInfo.SetValue(target, int.TryParse(value, out n) ? n : 0);
                break;

            case "double":
                double d;
                fieldInfo.SetValue(target, double.TryParse(value, out d) ? d : 0);
                break;

            case "string":
                fieldInfo.SetValue(target, value);
                break;
        }
    }

I have succeeded convert 1d string array to 1d original datatype array. like this

    public static void SetFieldValue(Object target, FieldInfo fieldInfo, string[] arr)
    {
        string fieldType = fieldInfo.FieldType.Name;
        fieldType = fieldType.ToLower();
        fieldType = fieldType.Replace("[]", "");

        switch (fieldType)
        {
            case "boolean":
                bool b;
                bool[] arr_b = Array.ConvertAll(arr, s => bool.TryParse(s, out b) ? b : false);
                fieldInfo.SetValue(target, arr_b);
                break;

            case "int32":
                int n;
                int[] arr_n = Array.ConvertAll(arr, s => int.TryParse(s, out n) ? n : 0);
                //int[] arr_n1 = Array.ConvertAll(arr, int.Parse);
                //int[] arr_n2 = arr.Select(s => int.TryParse(s, out n) ? n : 0).ToArray();
                fieldInfo.SetValue(target, arr_n);
                break;

            case "double":
                double d;
                double[] arr_d = Array.ConvertAll(arr, s => double.TryParse(s, out d) ? d : 0);
                fieldInfo.SetValue(target, arr_d);
                break;

            case "string":
                fieldInfo.SetValue(target, arr);
                break;
        }
    }

but, what should i do when handle 2D array?

    public static void SetFieldValue(Object target, FieldInfo fieldInfo, string[,] arr)
    {
        string fieldType = fieldInfo.FieldType.Name;
        fieldType = fieldType.ToLower();
        fieldType = fieldType.Replace("[,]", "");

        int n;
        double d;
        bool b;
        switch (fieldType)
        {
            case "boolean":
                //bool[] arr_b = Array.ConvertAll(arr, s => bool.TryParse(s, out b) ? b : false);
                //fieldInfo.SetValue(target, arr_b);
                break;

            case "int32":
                //int[,] arr_n = Array.ConvertAll(arr, s => int.TryParse(s, out n) ? n : 0);
                //fieldInfo.SetValue(target, arr_n);
                break;

            case "double":
                //double[,] arr_d = Array.ConvertAll(arr, s => double.TryParse(s, out d) ? d : 0);
                //fieldInfo.SetValue(target, arr_d);
                break;

            case "string":
                fieldInfo.SetValue(target, arr);
                break;
        }
    }

and, what i need to solve this problem is because I can't get GetType().GetField of array position. I gave up to get GetField of array[n_th,m_th] of array[row,col].

yeminfa
  • 13
  • 1
  • 7

4 Answers4

3

The easiest way is to just loop over the array:

string[,] strings = new string[,] { { "1", "2", "3" }, { "4", "5", "6" } };
int[,] ints = new int[strings.GetLength(0), strings.GetLength(1)];

for (int i = 0; i < strings.GetLength(0); i++)
{
    for (int j = 0; j < strings.GetLength(1); j++)
    {
        int.TryParse(strings[i, j], out ints[i, j]);
    }
}
user1793963
  • 1,295
  • 2
  • 12
  • 24
  • do you really need to `TryParse` instead of `Parse` there? – bashis Jan 22 '16 at 11:20
  • @bashis That depends on your specific needs. Are you 100% sure the values can be parsed correctly and what should happen if they're not? `TryParse` will fill in 0, `Parse` will throw an exception. – user1793963 Jan 22 '16 at 12:06
  • yeah, and with this approach you will have no chance to figure out if zeroes in the result were actually zeroes in the original array. I don't think this is the desirable behaviour in most cases. – bashis Jan 22 '16 at 12:09
  • @bashis If the results are meant to be nullable ints, then definitely consider revising. Otherwise you'll need a default of some sort and most of the time that's 0. – Balah Jan 22 '16 at 12:22
  • @Balah it all depends on the use-case, of course, but I _strongly_ believe that throwing an exception with `Parse` is almost always a better option than returning default zeroes. – bashis Jan 22 '16 at 12:26
  • @bashis Don't worry I do agree with you 100% (silent failures suck), but lets see what the OP needs ;) – Balah Jan 22 '16 at 12:42
  • The main reason I used `TryParse` here was because the OP also uses it in his code examples. It's a bit strange he doesn't use the return value (success or fail) in any meaningful way but I'm assuming it's a short (and much more performant) version of `try{ i=int.Parse(s); } catch { i=0; }`. – user1793963 Jan 22 '16 at 13:43
  • I'm working at company making factory auto machine. so, sometimes recovery function is better than occur error. this is what i used tryparse function. – yeminfa Jan 25 '16 at 07:03
  • if data type is double, need i allocate another array for that? – yeminfa Jan 25 '16 at 07:06
  • @yeminfa Yes, you would need a 2D array of doubles. If you combining the example above with your own code it should be easy to add other types. – user1793963 Jan 25 '16 at 12:37
1

Please Refer following sample code, and apply it your code.

static string[,] ToStringArray(object arg)
{
    string[,] result = null;

    if (arg is Array)
    {
        int rank = ((Array)arg).Rank;
        if (rank == 2)
        {
            int columnCount = ((Array)arg).GetUpperBound(0);
            int rowCount = ((Array)arg).GetLength(0);
            result = new string[rowCount, columnCount];

            for (int i = 0; i < rowCount; i++)
            {
                for (int j = 0; j < columnCount; j++)
                {
                    result[i, j] = ((Array)arg).GetValue(i, j).ToString();
                }
            }
        }
    }

    return result;
}
Gehan Fernando
  • 1,221
  • 13
  • 24
0

Looks like you were nearly there. You can try something like this:

Change your function something similar to @user1793963's answer, create a dynamic array in one switch statement (which will allow it to be of any type) and put the case statements inside the loop. so it will look like the below:

public static void SetFieldValue(Object target, FieldInfo fieldInfo, string[,] arr)
{
    string fieldType = fieldInfo.FieldType.Name;
    fieldType = fieldType.ToLower();
    fieldType = fieldType.Replace("[,]", "");

    int n;
    double d;
    bool b;
    dynamic output;

    // this should be in a different method
    switch (fieldType)
    {
        case "boolean":
            output = new bool[arr.GetLength(0), arr.GetLength(1)];
            break;

        case "int32":
            output = new int[arr.GetLength(0), arr.GetLength(1)];
            break;

        case "double":
            output = new double[arr.GetLength(0), arr.GetLength(1)];
            break;

        default:
            output = new string[arr.GetLength(0), arr.GetLength(1)];
            break;
    }

    for (int i = 0; i < arr.GetLength(0); i++)
    {
        for (int j = 0; j < arr.GetLength(1); j++)
        {
            switch (fieldType)
            {
                case "boolean":
                    output[i, j] = bool.TryParse(arr[i, j], out b) ? b : false;
                    break;

                case "int32":
                    output[i, j] = int.TryParse(arr[i, j], out n) ? n : 0;
                    break;

                case "double":
                    output[i, j] = double.TryParse(arr[i, j], out d) ? d : 0;
                    break;

                default:
                    output[i, j] = arr[i, j];
                    break;
            }
        }
    }

    fieldInfo.SetValue(target, output);
}

On a side-note, since you're serializing I'd just use either a JavaScriptSerializer (preferred method. use Newtonsoft JSON.Net), XmlSerializer or use a BinaryFormatter to deep clone then you won't need to do all the conversions back and forth manually. This question over here has a good answer on how to serialize dictionaries: Saving a Dictionary<String, Int32> in C# - Serialization?.


If that still does not help, update your question with how you call SetFieldValue so we can see what is happening.

Community
  • 1
  • 1
Balah
  • 2,530
  • 2
  • 16
  • 24
  • Thanks for your help. when execute your code "fieldInfo.SetValue(target, output);", an exception is occured. that exception is 'ArgumentExcption was unhandled', because fieldInfo.SetValue method is not support automatic cast from object type to specific type. and i cant call the method like this "fieldInfo.SetValue(target, (fieldInfo.FieldType)output" so, maybe i must preallocate each type's array variable for this problem. – yeminfa Jan 25 '16 at 06:38
  • and what i'm not using serializer is automatically create sql command for save/load data on database by using dictionary. if just support save/load data on file format, maybe I choose serializer function. As I am newbie on c#, if there is anything not correct, tell me what i'm wrong. – yeminfa Jan 25 '16 at 06:46
  • The code is full of errors (it was more of a thought process, than a working solution). I will update it now. For serializing, look at the answer to this question. That will allow you to save/load data of file format/database, or any source: http://stackoverflow.com/questions/4021893/saving-a-dictionarystring-int32-in-c-sharp-serialization – Balah Jan 25 '16 at 09:18
0

Because fieldInfo.SetValue function do not support implicit conversion, I need to allocate arrays for each data type even though i don't want. so, here is my method. thanks for your help, Balah, user1793963.

    public static void SetFieldValue(Object target, FieldInfo fieldInfo, string[,] arr)
    {
        string fieldType = fieldInfo.FieldType.Name;
        fieldType = fieldType.ToLower();
        fieldType = fieldType.Replace("[,]", "");

        // 0. string return
        switch (fieldType)
        {
            case "string":
                fieldInfo.SetValue(target, arr);
                return;
                break;
        }

        // 1. initialize
        int n;
        double d;
        bool b;

        //object[,] output = new object[arr.GetLength(0), arr.GetLength(1)];
        int[,] output_n = new int[arr.GetLength(0), arr.GetLength(1)];
        bool[,] output_b = new bool[arr.GetLength(0), arr.GetLength(1)];
        double[,] output_d = new double[arr.GetLength(0), arr.GetLength(1)];

        // 2. convert
        for (int i = 0; i < arr.GetLength(0); i++)
        {
            for (int j = 0; j < arr.GetLength(1); j++)
            {
                switch (fieldType)
                {
                    case "boolean":
                        output_b[i, j] = bool.TryParse(arr[i, j], out b) ? b : false;
                        break;

                    case "int32":
                        output_n[i, j] = int.TryParse(arr[i, j], out n) ? n : 0;
                        break;

                    case "double":
                        output_d[i, j] = double.TryParse(arr[i, j], out d) ? d : 0;
                        break;
                }
            }
        }

        // 2. setvalue
        //fieldInfo.SetValue(target, output);
        switch (fieldType)
        {
            case "boolean":
                fieldInfo.SetValue(target, output_b);
                break;

            case "int32":
                fieldInfo.SetValue(target, output_n);
                break;

            case "double":
                fieldInfo.SetValue(target, output_d);
                break;
        }
    }
yeminfa
  • 13
  • 1
  • 7