locked
Setting instance variables using an array RRS feed

  • Question

  • Hi all,

    I had a question about the possibility of setting an object's instance variables using an array, and how would I go about it.

    I have an object that is created from the rows of a spreadsheet which as been imported into a DataTable, and its instance variables represent the column data of the spreadsheet/datatable.

    To give a simplified example of what I am trying to accomplish, let's pretend I am working with some kind of progress check list.

    class ProgressChecklist
    {
    	class ProgressChecklist
    	{
    		public string name { get; set; }
    		public string company { get; set; }
    		public bool task1 { get; set; }
    		public bool task2 { get; set; }
    		public bool task3 { get; set; }
    
    		public ProgressChecklist(DataRow row)
    		{
    			// Assume the columns in the spreadsheet/datatable are Name | Company | Task 1 | Task 2 | Task 3
    			string[] string_headers = { "Name", "Company" };
    			string[] string_data = { this.name, this.company };
    
    			string[] bool_headers = { "Task 1", "Task 2", "Task 3" };
    			bool[] bool_data = { this.task1, this.task2, this.task3 };
    
    			for(int s = 0; s < string_headers.Length; s++)
    			{
    				string_data[s] = stringFromDataRow(data, string_headers[s]);
    			}
    
    			for(int b = 0; b < bool_headers.Length; b++)
    			{
    				string_data[b] = boolFromDataRow(data, string_headers[b]);
    			}
    		}
    
    		private string stringFromDataRow(DataRow data, string header)
    		{
    			// Returns the value of the cell
    			if (data[header].ToString().Count() > 0)
    			{
    				return data[header].ToString();
    			}
    			else
    			{
    				return string.Empty;
    			}
    		}
    
    		private bool boolFromDataRow(DataRow data, string header)
    		{
    			// Returns TRUE if cell has been marked off with an x; returns FALSE otherwise
    			if (data[header].ToString().Count() > 0)
    			{
    				return data[header].ToString().ToLower() == "x";
    			}
    			else
    			{
    				return false;
    			}
    		}
    	}
    }



    The issue I'm having is that assigning values to string_data[s] and bool_data[b] do not assign values to the instance variables themselves, but rather to the variables of the respective arrays.

    Is there any way to achieve what I am trying to do above, so I don't have to tediously do the following for a couple dozen instance variables?

    		public ProgressChecklist(DataRow person)
    		{
    			this.name = stringFromDataRow(row, "Name");
    			this.company = stringFromDataRow(row, "Company");
    			this.task1 = boolFromDataRow(row, "Task 1");
    			this.task2 = boolFromDataRow(row, "Task 2");
    			this.task3 = boolFromDataRow(row, "Task 3");
    		}

    Your help is greatly appreciated!

    Thomas

    Sunday, November 25, 2018 1:00 PM

All replies

  • Hi Thomas Lu (Wesgroup),

    Thank you for posting here.

    >>Is there any way to achieve what I am trying to do above, so I don't have to tediously do the following for a couple dozen instance variables?

    For your question, do you define the same data every time? If yes, you could define the string_data[s] and bool_data[b] out of the method. If no, please provide more details.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, November 26, 2018 6:16 AM
    Moderator
  • Hi Wendy,

    Thanks for your reply, although I am not quite sure you mean by defining the data.

    My Object class obviously has many more instance variables than in the example given, hence why I am trying to mitigate having to write repetitive code for every single variable.

    The DataTable's headers remain the same, but the values of the cells may be different.

    Pretending the DataTable looks like below:

    Name | Company | Task 1 | Task 2 | Task 3 |
    John | ABC XYZ |    x   |        |    x   |
    Paul | EFG UVW |    x   |        |        |
    Jane | ABC XYZ |    x   |    x   |    x   |
    I am trying to create an Object for each of John, Paul, and Jane, and set the values of their variables by iterating through an array as in my first post, instead of needing to individually call the method for each variable.

    Thanks.

    Monday, November 26, 2018 9:42 PM
  • To do what you ask, you'd need the C concept of "pointer to string", or "reference to string" and C# simply does not have that concept.  If you were using objects, you could do this; your array of objects would actually refer to the object you want to change.  But for a string, no.  As you have surmised, this line:

        string[] string_data = { this.name, this.company };

    does not create an array of references.  Instead, it fetches the value of those two properties, and stores those strings in the array.

    You will need to have verbose import/export functions, like you have at the end of your message.  This is a very common paradigm in C#.


    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    Tuesday, November 27, 2018 8:26 PM
  • Hi Thomas,

    you can use a dictionary that holds the values and uses them as your properties backing fields, also you can pass a list of mappings of headers and properties names, this way you can get your values using their header names and store them on a dictionary based on their property name: 

    class ProgressChecklist
    {
        private Dictionary<string, Tuple<string, object>> propertiesToValues;// propertyNamesToRowValuesMap;
        private Dictionary<string, object> values;
        public string name { get => values[nameof(name)].ToString(); }
        public string company { get => values[nameof(company)].ToString(); }
        public bool task1 { get => values[nameof(task1)].ToString().ToLower().Trim() == "x"; }
        public bool task2 { get => values[nameof(task2)].ToString().ToLower().Trim() == "x"; }
        public bool task3 { get => values[nameof(task3)].ToString().ToLower().Trim() == "x"; }
    
        public ProgressChecklist(DataRow row, Dictionary<string, string> headerToPropertiesMap)
        {
            values = new Dictionary<string, object>();
            foreach (var current in this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                if (headerToPropertiesMap.ContainsKey(current.Name)) {
                    var value = row[headerToPropertiesMap[current.Name]];
                    values.Add(current.Name, value);
                }
            }
            // propertiesToValues = new Dictionary<string, Tuple<string, object>>(); 
        }
    }

    and to use this solution you pass the data row and a mapping dictionary:

    var headerToPropertiesMap = new Dictionary<string, string>
    {
        { "name",  "Name" },
        { "company", "Company" },
        { "task1", "Task 1" },
        { "task2", "Task 2" },
        { "task3", "Task 3" }
    };
    var checkLists = new List<ProgressChecklist>(); 
    foreach (DataRow row in yourRowsCollection)
    {
        var current = new ProgressChecklist(row, headerToPropertiesMap);
        checkLists.Add(current);
    }

    another solution consists of using attributes: 

    class ProgressChecklist
    {
        private Dictionary<string, object> values;
        [DisplayName("Name")]
        public string name { get => values[nameof(name)].ToString(); }
        [DisplayName("Company")]
        public string company { get => values[nameof(company)].ToString(); }
        [DisplayName("Task 1")]
        public bool task1 { get => values[nameof(task1)].ToString().ToLower().Trim() == "x"; }
        [DisplayName("Task 2")]
        public bool task2 { get => values[nameof(task2)].ToString().ToLower().Trim() == "x"; }
        [DisplayName("Task 3")]
        public bool task3 { get => values[nameof(task3)].ToString().ToLower().Trim() == "x"; }
    
        public ProgressChecklist(DataRow row)
        {
            values = new Dictionary<string, object>();
            foreach (var current in this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                var attr = current.GetCustomAttribute<DisplayNameAttribute>();
                values.Add(current.Name, row[attr.DisplayName]);
            }
        }
    }

    and you use it in a simple way:

    var checkLists = new List<ProgressChecklist>(); 
    foreach (DataRow row in yourRowsCollection)
    {
        var current = new ProgressChecklist(row);
        checkLists.Add(current);
    }

    Hope it helps;

    Best Regards,

    Mouad.

    Good Coding;


    Tuesday, November 27, 2018 11:23 PM
  •     string[] string_data = { this.name, this.company };

    does not create an array of references.  Instead, it fetches the value of those two properties, and stores those strings in the array.


    In fact, the statement above, does create an array of references.

    Wednesday, November 28, 2018 6:50 AM
  • Hi ...,

    Thanks for your reply, although I am not quite sure you mean by defining the data.

    My Object class obviously has many more instance variables than in the example given, hence why I am trying to mitigate having to write repetitive code for every single variable.

    The DataTable's headers remain the same, but the values of the cells may be different.

    Pretending the DataTable looks like below:

    Name | Company | Task 1 | Task 2 | Task 3 |
    John | ABC XYZ |    x   |        |    x   |
    Paul | EFG UVW |    x   |        |        |
    Jane | ABC XYZ |    x   |    x   |    x   |
    I am trying to create an Object for each of John, Paul, and Jane, and set the values of their variables by iterating through an array as in my first post, instead of needing to individually call the method for each variable.

    Thanks.

    The way you describe your intention, is a bit confusing, because in the OP you mention arrays (in ctor) and non-array properties.

    Anyway, I'm sure you can accomplish your intent by simple use of Array.Copy(...).

    If you describe your 'problem' better (4me), I can post some C# code...


    • Edited by ritehere44 Wednesday, November 28, 2018 6:56 AM
    Wednesday, November 28, 2018 6:55 AM
  • You are correct, although the net result is the same.  It is an array of references to objects that cannot be changed.  If this were some other type of object, we could modify the object itself using class methods, and all of the references to that object would see the change.  Strings cannot be modified, so the only way to change the string that this.name refers to is to assign a new string to this.name, and there's no way to do that through references.

    Tim Roberts | Driver MVP Emeritus | Providenza &amp; Boekelheide, Inc.

    Wednesday, November 28, 2018 10:20 PM
  • I guess we agree: that array holds reference variables (because string is a reference type, but, definitely, it does not hold reference to those variables (or properties).

    Strings cannot be modified, so the only way to change the string that this.name refers to is to assign a new string to this.name, and there's no way to do that through references.

    In fact, it's possible to change the value of a variable of type string (and others), through its reference, either thru (1) C# Unsafe, or thru (2) ref variables

    I'm posting below, C# code to demonstrate the above thru ref variables.

    Notice, however, that using a DataRow as input, it's possible to set the values of many instance/static variables at once, thru arrays and Array.Copy(), even in old C# compilers (the code is simple).


    • Edited by ritehere44 Thursday, November 29, 2018 6:33 AM
    Thursday, November 29, 2018 6:32 AM
  • // notice that the instance variables/fields 
    // are set thru their reference, 
    // and not directly.
    
    using System;
    namespace ConsoleApp1
    {
        class MyClass
        {
            public int I1, I2;
            public string S1, S2;
    
            public MyClass(int a, int b, string c, string d)
            {
                ref int i1 = ref GetI1(); ref int i2 = ref GetI2();
                ref string s1 = ref GetS1(); ref string s2 = ref GetS2();
                i1 = a; i2 = b; s1 = c; s2 = d;
    
                ref int GetI1() => ref I1; ref int GetI2() => ref I2;
                ref string GetS1() => ref S1; ref string GetS2() => ref S2;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                MyClass mc = new MyClass(123, 456, "qwe", "rty");
    
                Console.WriteLine($"{mc.I1}, {mc.I2}, {mc.S1}, {mc.S2}");
                Console.ReadLine();
            }
        }
    }

    Thursday, November 29, 2018 6:37 AM
  • // notice that the instance variables/fields 
    // are set thru their reference, 
    // and not directly.
    
    using System;
    namespace ConsoleApp1
    {
        class MyClass
        {
            public int I1, I2;
            public string S1, S2;
    
            public MyClass(int a, int b, string c, string d)
            {
                ref int i1 = ref GetI1(); ref int i2 = ref GetI2();
                ref string s1 = ref GetS1(); ref string s2 = ref GetS2();
                i1 = a; i2 = b; s1 = c; s2 = d;
    
                ref int GetI1() => ref I1; ref int GetI2() => ref I2;
                ref string GetS1() => ref S1; ref string GetS2() => ref S2;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                MyClass mc = new MyClass(123, 456, "qwe", "rty");
    
                Console.WriteLine($"{mc.I1}, {mc.I2}, {mc.S1}, {mc.S2}");
                Console.ReadLine();
            }
        }
    }

    I'm sorry I cannot see code like this without changing it. you should format it better so it's easier to read for everyone.

    // notice that the instance variables/fields 
    // are set thru their reference, 
    // and not directly.
    
    using System;
    namespace ConsoleApp1
    {
        class MyClass
        {
            public int I1, I2;
            public string S1, S2;
    
            public MyClass(int a, int b, string c, string d)
            {
                ref int i1 = ref GetI1(); 
                ref int i2 = ref GetI2();
                ref string s1 = ref GetS1(); 
                ref string s2 = ref GetS2();
                i1 = a; 
                i2 = b; 
                s1 = c; 
                s2 = d;
    
                ref int GetI1() => ref I1; 
                ref int GetI2() => ref I2;
                ref string GetS1() => ref S1; 
                ref string GetS2() => ref S2;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                MyClass mc = new MyClass(123, 456, "qwe", "rty");
    
                Console.WriteLine($"{mc.I1}, {mc.I2}, {mc.S1}, {mc.S2}");
                Console.ReadLine();
            }
        }
    }

    One time in future, if you are working in a team, you will thank me ;)

    Friday, March 15, 2019 9:27 AM
  • @moderators


    I'm sorry I cannot see code like this without changing it. you should format it better so it's easier to read for everyone.

    // notice that the instance variables/fields 
    // are set thru their reference, 
    // and not directly.
    
    using System;
    namespace ConsoleApp1
    {
        class MyClass
        {
            public int I1, I2;
            public string S1, S2;
    
            public MyClass(int a, int b, string c, string d)
            {
                ref int i1 = ref GetI1(); 
                ref int i2 = ref GetI2();
                ref string s1 = ref GetS1(); 
                ref string s2 = ref GetS2();
                i1 = a; 
                i2 = b; 
                s1 = c; 
                s2 = d;
    
                ref int GetI1() => ref I1; 
                ref int GetI2() => ref I2;
                ref string GetS1() => ref S1; 
                ref string GetS2() => ref S2;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                MyClass mc = new MyClass(123, 456, "qwe", "rty");
    
                Console.WriteLine($"{mc.I1}, {mc.I2}, {mc.S1}, {mc.S2}");
                Console.ReadLine();
            }
        }
    }

    One time in future, if you are working in a team, you will thank me ;)

    Where are you all, moderators!

    This is 1 more example that this coward freaking queer student SOB created a fraudulent account, just to annoy people and the forum; *it*'s NOT contributing to nothing.

    Also, notice that I'm NOT a programmer (never was); I make programs for fun!

    I do not work in a team; the team works for me!

    And I never select a team member that understands programming less than me.

    In a team like this one, this coward queer freaking student SOB, can only clean the toilet, without permission to take the eyes off the floor.

    And, if *it* doesn't work correctly, it'll be shoved into the latrine and the FLUSH button, pressed.

    Monday, March 18, 2019 3:23 AM
  • Dear ritehere, it's nice that for the sake of clarity you wrote that you are not a programmer, although it is obvious and I knew from the very beginning that you are not a person from the industry. I will give you good and last advice and I hope that you will take it to heart. 
    MSDN Forum were mainly created to acquire new knowledge or to share your own, unfortunately, by people like you who post things that do not comply with the truth, it can only hurt.
    Do it for the good of msdn society and please do not add any comments from yourself, I know you want to do well, but with your lack of technical background you can only hurt young programmers trying to learn something new or take lessons on their mistakes.
    I hope that I will never have to participate in any conversation with you.
    Regards, wholeheartedly MSDN community

    Refactoring Pinguin


    Monday, March 18, 2019 1:33 PM