首页 > 解决方案 > 使用反射显示来自不同自定义类的数据

问题描述

我想创建一个显示对象中包含的信息的方法,该方法将动态地与任何对象一起工作。我在处理属于其他自定义类的属性时遇到问题。在下面的示例中,PersonhasPhonesOccupationswhich 都是其他类。数据显示时,当前屏幕上的值为:

TestReflection.Person
Name: Mary
Phones: TestReflection.Phones
Occupations: TestReflection.Occupations

它只显示类的名称,例如TestReflection.Phones,而不是该对象内的数据。

如何更改此代码以显示此类信息?

TestReflection.Person
Name: Mary
Phones:
    TestReflection.Phones
    Type: 1
    Number: 555XYZ
Occupations: 
    TestReflection.Occupations
    Type: 5
    Description: Secretary

这是我的代码:

class Program
{
    static void Main(string[] args)
    {
        List<Person> listPeson = new List<Person>();
        var person1 = new Person();
        person1.Name = "Mary";
        person1.Phones = new  Phones { new Phone { Type = 1, Number = "555XYZ" } };
        person1.Occupations = new Occupations {new Occupation { Type = 5, Description = "Secretary" }};
        listPeson.Add(person1);
        DynamicExport(listPeson);
        Console.ReadLine();
    }

    public static void DynamicExport<T>(List<T> listReg)
    {

        for (int i = 0; i < listReg.Count; i++)
        {
            Console.WriteLine(listReg[i].GetType());
            foreach (var item in listReg[i].GetType().GetProperties())
            {
                Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");
            }
        }
    }
}


class Person
{
    public string Name { get; set; }
    public Phones Phones { get; set; }
    public Occupations Occupations { get; set; }
}

class Phones : List<Phone> { }
class Phone
{
    public int Type { get; set; }
    public string Number { get; set; }
}

class Occupations : List<Occupation> { }
class Occupation

{
    public int Type { get; set; }
    public string Description { get; set; }
}

标签: c#serializationreflection

解决方案


我对您的问题进行了一些编辑-希望我正确理解了您。

如果要导出数据

If your question is really about displaying data, then there are better ways to do it than creating your own export method. The format you are trying to display looks similar to YAML. There's also JSON and XML. Using one of these libraries is probably better than writing your own method:

If you want to learn more about reflection

Maybe you're interested in learning more about reflection, and the export is just an example to play around with it. In that case, let's look at this line:

Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");

$"{item.GetValue(listReg[i], null)}" ends up calling person1.Phones.ToString(). The default behavior of ToString just displays the type name. You could override that behavior, like this:

class Phones : List<Phone>
{
    public override string ToString()
    {
        return Program.DynamicExportToString(this);

        // ... where DynamicExportToString is a modified version of DynamicExport that
        // builds and returns a string rather than sending it directly to the Console.
    }
}

Maybe you want to be able to handle any class, even when you cannot override ToString in all of the classes you might export. Then you will need to put some additional logic in the DynamicExport method, because...

$"{item.Name}: {item.GetValue(listReg[i], null)}"

... doesn't work for every situation. We need to display different things depending on the type of the property.

  • Consider how you want to handle null values. Maybe something like $"{item.Name}: <null>"
  • Use your existing $"..." code if the type is...
    • a primitive type.
    • DateTime
    • String
    • ... or a Nullable<> of one of those types.
  • If the type implements IEnumerable, loop over the contents of the collection and recursively call your export code for each element.
    • It's important to check for this interface after you've checked if the type is a String, because String implements IEnumerable.
  • Otherwise, recursively call your export code on this value.

When you call your export code recursively, it would be wise to guard against infinite loops. If the object you're trying to export contains a circular reference - you could quickly wind up with a StackOverflowException. To avoid this, maintain a stack of objects that have already been visited.

I think the above advice is generally applicable whenever you're using reflection to traverse an object graph - whether it's for serialization or any other purpose. I hope this helps!


推荐阅读