-2

I have a class as follows :

    class Student 
    {
        private string name;
        private string family;
        private string id;

        public string[] fields = {"name", "family", "id"};

        public Student(string name,string family,string id)
        {
            this.name = name;
            this.family = family;
            this.id = id;
        }              
    }

Now in another class I want to loop through members defined in fields array. This is my code that doesn't work

    class StudentController
    {
        Student student;

        public StudentController(Student st)
        {
            this.student = st;

        }
        public void store()
        {
             foreach(string f in this.student.fields)
                    Console.WriteLine(this.student.f);
        }
    }
M a m a D
  • 1,938
  • 2
  • 30
  • 61
  • 1
    This is possible using [reflection](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection) – Steve Dec 25 '20 at 15:20
  • Hint: `typeof(Student).GetField("fields")` – dcg Dec 25 '20 at 15:25
  • @dcg Im new to c#, and I use sql server 2013. Is this code supported there? – M a m a D Dec 25 '20 at 15:26
  • Using the mentioned reflection is the key, and then you don't even need the string array. You can enumerate the fields directly. – Dialecticus Dec 25 '20 at 15:27
  • @MamaD you can check [here](https://learn.microsoft.com/en-us/dotnet/api/system.type?view=net-5.0) the `Type` docs and in what frameworks (and versions) it's available. – dcg Dec 25 '20 at 15:31
  • @Dialecticus I don't want to loop through all the members and the controller is unaware of Student members. it only knows the fields array. – M a m a D Dec 25 '20 at 15:33
  • As many pointed out, reflection is the way to do exactly this, _but_, depending on what you actually want to do with those fields (do you really only want to `Console.WriteLine()` their values?), using reflection might be very well the worst possible solution to your problem. Can you provide more details what your intention is? – Good Night Nerd Pride Dec 25 '20 at 16:02

3 Answers3

2

Field name is not a stand-in for field's value, so this.student.f refers to the non-existent field or property called f of your Student object, causing the error.

There are multiple ways of fixing this, depending on your taste. For example, you can use reflection to get field's value (the answer at the link talks about properties; you can adjust it to use fields, or switch to properties, which is a more C#-ish way to do things). An added bonus is that you no longer need to define the list of all fields, unless you want to, because C# lets you get all fields for free.

Alternatively, you could define a collection of Func<Student,string>, where the functor accesses a particular field:

public Dictionary<string,Func<Student,string>> fields = {
    ["name"] = s => s.name
,   ["family"] = s => s.family
,   ["id"] = s => s.id
};

Now you can iterate over the fields and print names and values as you go:

foreach(string p in student.fields)
    Console.WriteLine("{0} = {1}", p.Key, p.Value(student));
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    I'd try very hard to keep a person completely new at c# away from Reflection. There are many ways to solve this simple problem that don't require something that can go bad so easily – Camilo Terevinto Dec 25 '20 at 15:34
  • 1
    @CamiloTerevinto I wouldn't call the OP a completely new person to C#. Apart from this particular misunderstanding, he's pretty good with the syntax, so I'm 100% certain that he's good to go on learning reflection. – Sergey Kalinichenko Dec 25 '20 at 15:36
  • 1
    I'll refer you to [this comment](https://stackoverflow.com/questions/65449344/c-how-to-loop-through-members-defined-via-an-array-in-a-class#comment115711460_65449344). There's still no reason to use Reflection here, at all. Use an array, an enumerator, a `ToString()` overload as @Steve showed, any simple method will do, will be faster and cleaner – Camilo Terevinto Dec 25 '20 at 15:38
  • Is this some new feature in the latest C# syntax? I am not able to compile your dictionary initialization. And in the final foreach probably you want to change _string p_ to _KeyValuePair>_ – Steve Dec 26 '20 at 18:38
  • 1
    @Steve [This is a C# 6 feature](https://visualstudiomagazine.com/Blogs/Tool-Tracker/2015/04/C-Sharp-Dictionary-Initializers.aspx). – Sergey Kalinichenko Dec 26 '20 at 20:05
1

Usually these kind of tasks are resolved using Reflection. The Reflection api system allows you to examine the Type (in this case an instance of a Student) and ask about its properties and methods associated with it. The advantage is that you don't need a public field array of strings and, if you add new members, the code that traverse your properties doesn't change. The downside is the relatively performance impact of using reflection (but to be really concerned you should always test in your real environment)

But you could also implement a simple alternative creating your own version of the ToString method.

For example, extend your Student class to be

public class Student
{
    private string name;
    private string family;
    private string id;

    public string[] fields = { "name", "family", "id", "all" };

    public Student(string name, string family, string id)
    {
        this.name = name;
        this.family = family;
        this.id = id;
    }
    public string ToString(string field)
    {
        switch (field)
        {
            case "name":
                return this.name;
            case "family":
                return this.family;
            case "id":
                return this.id;
            case "all":
            default:
                return $"{name}, {family}, {id}";
        }
    }
}

Now you can loop over the fields with

Student t = new Student("John", "McEnroe", "134");
foreach(string s in t.fields)
    Console.WriteLine(t.ToString(s));

or just for a single field with

Console.WriteLine(t.ToString("name"));
Steve
  • 213,761
  • 22
  • 232
  • 286
0

If you're trying to access the values in

public string[] fields = {"name", "family", "id"};

Then just change this:

foreach(string f in this.student.fields)
    Console.WriteLine(this.student.f);

To this:

foreach(string f in this.student.fields)
    Console.WriteLine(f);

You were trying to access the variable f as like it was a member of StudentController.

However, if you're trying to access the values of name, family, and id that were passed to the Student constructor, then turn this fields to properties like this:

public string Name { get; set; }
public string Family { get; set; }
public string Id { get; set; }

And use the following code to access them:

foreach(var property in typeof(Student).GetProperties()) {
    var propValue = property.GetValue(student);
    Console.WriteLine(propValue);
}

If you those properties to only be set from the Student class, then you can either do this:

public string Name { get; }

or this:

public string Name { get; private set; }
Salih Kavaf
  • 897
  • 9
  • 17