0

I have a foreach loop and I would like to call all properties of a certain class in the foreach loops so I don't have to write it all out.

The class I have created

public Person()
    {
        firstname = "";
        surname = "";
        haircolor = "";
        eyecolor = "";
        weight = 0;
        height = 0;
        age = 0;
    }

This is the code I am trying to compact

  Console.WriteLine("Please enter the next persons firstname");//new person user input (firstname)
  addperson.firstname = Console.ReadLine();

  Console.WriteLine("Enter the persons surname");//surname
  addperson.surname = Console.ReadLine();

  Console.WriteLine("Enter " + addperson.name + "'s hair color");//hair color
  addperson.haircolor = Console.ReadLine();

  Console.WriteLine("Enter the age of " + addperson.firstname);//age
  addperson.age = Convert.ToInt32(Console.ReadLine());

  Console.WriteLine("Enter the weight of " + addperson.firstname);//weight
  addperson.weight = Convert.ToDouble(Console.ReadLine());

  Console.WriteLine("Enter the height of " + addperson.firstname);//height
  addperson.height = Convert.ToDouble(Console.ReadLine());

I have started on the foreach loop, I would like a way to compact all of that code to a loop

  foreach (Person.)
            {
                Console.WriteLine("Please enter " +addperson.ToString);
                  Person.addperson = Console.ReadLine();
            }

Any help would be much appreciated

Jack
  • 3
  • 3
  • 4
    possible duplicate of [How to loop through all the properties of a class?](http://stackoverflow.com/questions/531384/how-to-loop-through-all-the-properties-of-a-class) – fubo Apr 08 '15 at 12:46
  • 6
    Personally I would leave it as it is, particularly as you need different type conversions – Alex K. Apr 08 '15 at 12:46
  • Keep it how it is, for both readability and type conversions. – Luke Joshua Park Apr 08 '15 at 12:50
  • Fubo's link shows how to do it but for your simple example its more hassle than its worth and it makes the code hard to read. – CathalMF Apr 08 '15 at 12:57
  • possible duplicate of [c# foreach (property in object)... Is there a simple way of doing this?](http://stackoverflow.com/questions/9893028/c-sharp-foreach-property-in-object-is-there-a-simple-way-of-doing-this) – Orace Apr 08 '15 at 13:43

3 Answers3

2

you need to use reflection to be able to loop over each property dynamically, personally i wont change it to use reflection since reflection has performance penalties, but here is the code for your reference:

Change Class fields to properties:

    public class Person
    {
      public string firstname {get;set;}
      public string surname {get;set;}
      public string haircolor {get;set;}
      public string eyecolor {get;set;}
      public string weight {get;set;}
      public string height {get;set;}
      public string age {get;set;}
     }

Write this code in your main method:

       Person addperson = new Person();
       PropertyInfo[] props = addperson.GetType().GetProperties();

        foreach(PropertyInfo prop in props)
        {
              Console.WriteLine("Please enter " + prop.Name.ToString());
              string input = Console.ReadLine();
              prop.SetValue(addperson, input, null);
        }

EDIT:

This line of code:

 PropertyInfo[] props = addperson.GetType().GetProperties();

Returns all the public properties of the Person type (class), then each PropertyInfo object discovers the attributes of the property and provides access to its metadata.

YazX
  • 444
  • 4
  • 12
  • Note that the (obviously incomplete) class he defined in the first post is composed of Fields, not Properties. He should really change them to properties, but if not, your reflection code needs to be changed to GetFields() instead of GetProperties(). Edit: Whoops, didn't notice you instructed him to change them to props. sorry. – irreal Apr 08 '15 at 12:59
  • 2
    And you don't really support his `int Age` :-) – xanatos Apr 08 '15 at 13:00
  • Could you just explain what 'PropertyInfo[] props = addperson.GetType().GetProperties();' does? – Jack Apr 08 '15 at 13:27
  • @Jack, it gets all properties in the class, ill update my comment to explain it. – YazX Apr 08 '15 at 13:29
  • 1
    @Jack This would also allow an age of "fire truck" or "death star"... no clue if that's valid for you =) – Florian Schmidinger Apr 08 '15 at 14:07
1

I'd do it like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace YourNameSpace
{
    class Program
    {
        static void Main(string[] args)
        {
            Person p = GetPersonFromUserInput();
        }

        private static Person GetPersonFromUserInput()
        {
            Person p = new Person();
            Type t = typeof(Person);
            foreach (PropertyInfo pi in t.GetProperties().Where(pi => pi.GetCustomAttribute<PromptAttribute>() != null))
            {
                PromptAttribute attribute = pi.GetCustomAttribute<PromptAttribute>();

                Console.Write("{0}: ", pi.GetCustomAttribute<PromptAttribute>().Prompt);

                if (pi.PropertyType == typeof(int))
                {
                    PromptInteger(p, pi);
                }
                else if (pi.PropertyType == typeof(string))
                {
                    PromptString(p, pi);

                } //add more types in this manner                           
            }

            return p;
        }

        private static void PromptString(Person p, PropertyInfo pi)
        {
            string userInput = Console.ReadLine();
            pi.SetMethod.Invoke(p, new object[] { userInput });
        }

        private static void PromptInteger(Person p, PropertyInfo pi)
        {
            int userInput;
            while (!int.TryParse(Console.ReadLine(), out userInput))
            {
                Console.Write("You have to enter an integer: ");
            }

            pi.SetMethod.Invoke(p, new object[] { userInput });
        }


    }

    public class Person
    {
        [Prompt("Please enter the persons firstname")]
        public string FirstName { get; set; }

        [Prompt("Please enter the persons surname")]
        public string SurName { get; set; }

        [Prompt("Please enter the persons haircolor")]
        public string HairColor { get; set; }

        [Prompt("Please enter the persons eyecolor")]
        public string EyeColor { get; set; }

        [Prompt("Please enter the persons weight")]
        public int Weight { get; set; }

        [Prompt("Please enter the persons height")]
        public int Height { get; set; }

        [Prompt("Please enter the persons age")]
        public int Age { get; set; }
    }

    public class PromptAttribute : Attribute
    {
        public string Prompt { get; private set; }

        public PromptAttribute(string prompt)
        {
            Prompt = prompt;
        }
    }
}

Using the custom - attribute you could define the prompt. if you have more types just add more else ifs in the loop.

Florian Schmidinger
  • 4,682
  • 2
  • 16
  • 28
  • You can use `Convert.ChangeType()` instead of a switch over the type. http://stackoverflow.com/questions/1089123/setting-a-property-by-reflection-with-a-string-value – Orace Apr 08 '15 at 13:07
  • @Orace but then i dont have control if the userinput is a valid integer(for example) do i? – Florian Schmidinger Apr 08 '15 at 13:09
  • 1
    It will probably trow exception (see the link). Also, you will have a `NullPointerException` if one property as no `PromptAttribute`. – Orace Apr 08 '15 at 13:20
  • That is true ill add that check to the example. however i'd rather not control my program flow with costy exceptions if the user makes a wrong input. – Florian Schmidinger Apr 08 '15 at 13:29
  • I think you can loose some micro-seconds on an exception when you ask user to enter data with keyboard... It's not high frequency traiding XML data. – Orace Apr 08 '15 at 13:31
0

A simple console app, using reflection, linq and a dictionary:

using statements:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

Your Class:

public class Person
{
  public string  firstname {get; set;}
  public string surname { get; set; }
  public string haircolor { get; set; }
  public string eyecolor { get; set; }
  public string weight { get; set; }
  public string height { get; set; }
  public string age { get; set; }
}

Console Application:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {

            var per = new Person();
            var perDict = new Dictionary<string, string>();


            foreach (
                var c in 
                    per.GetType()
                        .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                            .ToDictionary(prop => prop.Name, prop => prop.GetValue(per,null))
                        )
            {

            Console.Write("Greetings, Please Enter Your Value for: " + c.Key + " ");
            var answer = Console.ReadLine();
            perDict.Add(c.Key, answer);
            per.GetType().GetProperty(c.Key).SetValue(per,answer, null);
            }
        }
    }

}