0

We have a (test) class like this:

class Animal
{
    public string AnimalName { get; set; }
    public string AnimalType { get; set; }
    public int AnimalAgeInMonths { get; set; }
}

... and object (or reference) to class:

Animal animal = new Animal();
animal.AnimalName = null; // No name yet
animal.AnimalType = "Sheep";
animal.AnimalAgeInMonths = 7;

So I need a method that returns System.Collections.Generic.List<(string[], string[], string[])>. First string array represents names of all properties (i.e. AnimalName, AnimalType, etc), second array - values (i.e. null, Sheep, etc), and third array - data type, for example:

  • System.Int32 for int
  • System.Single for float
  • System.String for string
  • etc

(these can be generated with the typeof keyword).

So overall, I need a method like this:

public List<(string[], string[], string[])> GetClassPropertyInfo(object Class)

which can be called like this:

GetClassPropertyInfo(animal);

Just telling, I did search around 10 minutes to find the best matching answer, but found nothing for what I am looking for (for example, I would find an answer, but not how to get data type as well, or the other way around), and also attempted creating this code manually over 3 times, of course with the help of System.Reflection namespace, but it didn't work, Similar questions also didn't have any similar questions, and I eventually gave up.

I tried this (4th attempt):

using System;
using System.Reflection;
using System.Collections.Generic;

// FYI: Coded on an online compiler to save myself a lot of time

namespace TestAttempt
{
    public class Animal
    {
        public string AnimalName { get; set; }
        public string AnimalType { get; set; }
        public int AnimalAgeInMonths { get; set; }
    }
    
    public class Program
    {
        static void Main()
        {
            Animal a = new Animal();
            a.AnimalName = "";
            a.AnimalType = "Sheep";
            a.AnimalAgeInMonths = 7;
            
            List<(string[], string[], string[])> types = new Program().GetClassPropertyInfo(a, typeof(Animal));
            
            foreach (var Iteration in types)
            {
                for (int i = 0; i < Iteration.Item1.Length; i++)
                {
                    Console.WriteLine($"{Iteration.Item3[i]} {Iteration.Item1[i]} = {Iteration.Item2[i]}");
                }
            }
        }
        
        private List<(string[], string[], string[])> GetClassPropertyInfo(object Class, Type _Class)
        {
            var @return = new List<(string[], string[], string[])>();
            
            List<string> propertyNames = new List<string>();
            List<string> propertyTypes = new List<string>();
            List<string> propertyValues = new List<string>();
            
            // https://stackoverflow.com/questions/737151/how-to-get-the-list-of-properties-of-a-class
            foreach (var prop in Class.GetType().GetProperties())
            {
                propertyNames.Add(Convert.ToString(prop.Name));
                propertyValues.Add(Convert.ToString(prop.GetValue(Class, null)));
            }
            
            // System.Reflection.FieldInfo
            // See: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.fieldinfo?view=net-7.0
            FieldInfo[] fi = _Class.GetFields(BindingFlags.NonPublic | BindingFlags.Instance
            | BindingFlags.Public);
            
            for (int i = 0; i < fi.Length; i++)
                propertyTypes.Add(Convert.ToString(fi[i].FieldType));
                
            @return.Add((propertyNames.ToArray(), propertyValues.ToArray(), propertyTypes.ToArray()));
            
            return @return;
        }
    }
}

It works, but not when an array is a property.

Best regards,
-winscripter.

winscripter
  • 109
  • 1
  • 11
  • Can you show some of your attempts? – Sweeper Jun 10 '23 at 13:34
  • Does this answer your question? [Using PropertyInfo to find out the property type](https://stackoverflow.com/questions/3723934/using-propertyinfo-to-find-out-the-property-type) – marsh-wiggle Jun 10 '23 at 13:35
  • Also, why do you need a *list* of the 3 string arrays? From what you are describing, it seems like you just need 3 string arrays, period. Please write out each element of this list completely for the `Animal` object you showed, so that exactly what you want is clear. – Sweeper Jun 10 '23 at 13:36
  • @Sweeper well I was coding on an online compiler simply because I was trying if something works or not to save time, but I closed all these tabs. I tried my 4th attempt and edited my question. It does work, but not with arrays. With regards arrays, this is because a class can contain multiple properties and we want to fetch information about all properties in a class. – winscripter Jun 10 '23 at 13:59
  • Yeah I understand why you want arrays, but why do you want a *list* of those arrays? From your attempt, it seems like you only add one element to this list, so why not just return a `(string[], string[], string[])`, instead of `List<(string[], string[], string[])>`? – Sweeper Jun 10 '23 at 14:04
  • @Sweeper well I don't (currently) know a way to return multiple string arrays at once, rather than adding them to the list. – winscripter Jun 10 '23 at 14:09

2 Answers2

0

The following will get the name, type and value of each property (as strings) - you may use this as a starting point. You can then store the name, type and value in any structure you need and return it. Hope that helps.

using System;
using System.Collections;
using System.Reflection;

class Animal
{
    public string AnimalName { get; set; }
    public string AnimalType { get; set; }
    public int AnimalAgeInMonths { get; set; }
    public int[] array { get; set; } 
}

class Program
{
    static void Main()
    {

        Animal animal = new Animal();
        animal.AnimalName = null; // No name yet
        animal.AnimalType = "Sheep";
        animal.AnimalAgeInMonths = 7;
        animal.array = new int[] { 1, 3, 5, 7, 9 };
        
        class_info( animal );
            
    }
    
    static void class_info(object Class)
    {
        String val;
        
        foreach (PropertyInfo propertyInfo in Class.GetType().GetProperties())
        {
            // Property Name ...
            Console.WriteLine( propertyInfo.Name );

            // Property type (as string) ...
            Console.WriteLine( propertyInfo.PropertyType.ToString() );

            // Property value (as string) ...
            if ( propertyInfo.GetValue(Class, null) is null ) 
            {
                val = "null";
                Console.WriteLine( val );
            }
            else
            {
                // Property is an array, show values (as strings) ...
                if (propertyInfo.PropertyType.IsArray)
                {

                    IEnumerable array = propertyInfo.GetValue( Class, null ) as IEnumerable;

                    if ( array is null )
                    {
                        Console.WriteLine( "Array is null" );
                    }
                    else
                    {
                        Console.WriteLine("Array values");
                        Console.WriteLine("------------");

                        foreach (var element in array)
                            Console.WriteLine(element.ToString());
                    }

                }
                else
                {
                   val = propertyInfo.GetValue(Class, null).ToString();
                   Console.WriteLine( val );
                }
            }

        }  
        
    }
    
}
bdcoder
  • 3,280
  • 8
  • 35
  • 55
  • The code works, that's great, but just like my attempt, it returns `System.(array type)[]` as string. If an array contains values like these: { "Foo", "Bar"}, then we can return `{ "Foo", "Bar"}` as string, which is not difficult. Can you edit the code to add support for arrays, by any chance? – winscripter Jun 10 '23 at 14:47
  • @winscripter - code modified to include array property. Hope that helps. – bdcoder Jun 10 '23 at 15:09
0

It seems like you only want 3 arrays. Each of those can be easily obtained with the Reflection API.

static (string[], string?[], string[]) GetClassPropertyInfo(object o) {
    // pass binding flags here if you want a different set of properties than default behaviour
    var properties = o.GetType().GetProperties();
    return (
        properties.Select(x => x.Name).ToArray(),
        properties.Select(x => x.GetValue(o)?.ToString()).ToArray(),
        properties.Select(x => x.PropertyType.Name).ToArray()
    );
}

Regarding the second array - you need a way to convert any property value (including null) into a string. Here I have just called ToString, and if the value is null, the result is also null. This is why the second array has nullable elements, and is of type string?[].

Regarding the third array, Name returns the simple name, without the namespace. If you need that, you might want to do x.PropertyType.FullName ?? x.PropertyType.Name instead, or just x.PropertyType.ToString().

Also note that you can totally just return 3 things in a tuple, like I have done here, without putting them in a list.

That said, I would recommend creating a record type for this:

record ObjectInfo(string[] Name, string?[] Values, string[] Types);

And you'd instead write:

return new ObjectInfo(
    ...
);

and also consider that maybe they shouldn't all be strings. I'd think that this makes more sense:

record ObjectInfo(string[] Name, object?[] Values, Type[] Types);
Sweeper
  • 213,210
  • 22
  • 193
  • 313