Reflection C #, how does it work?

7

I'm doing some testing with reflection , I made this code on the basis of trial and error, so I did not understand how exactly it works.

This is the enum I'm using:

public enum TipoDoAmbiente
{

    [System.Xml.Serialization.XmlEnumAttribute("1")]
    Item1,


    [System.Xml.Serialization.XmlEnumAttribute("2")]
    Item2,
}

The propriedade.GetValue(objeto, null); method returns the value of enum , in case of this test I made the value is item1 . When I do a debug and stop at itemEnum nothing appears other than the value itself.

I get this value and step into the next methods, my question is how does the rest of the code "discover" the type and information of the member if I am just passing the value item1 ?

           Type tipo = objeto.GetType();
           foreach (PropertyInfo propriedade in tipo.GetRuntimeProperties())
            {
                Type tipoBase = propriedade.PropertyType.BaseType;

                if (tipoBase.Name == "Enum")
                {
                    var itemEnum = propriedade.GetValue(objeto, null);
                    var valorEnum= (Enum)itemEnum;
                    var infoEnum = valorEnum.GetType().GetMember(propriedade.GetValue(objeto, null).ToString()).FirstOrDefault();
                    var EnumAttribute = infoEnum.GetCustomAttribute<XmlEnumAttribute>().Name;

                }


            }

I'm testing the deserialization of an XML, and I get the object that was generated, that object that is used in the code.

What left me with the most doubt is that there are several enums with equal values, example item1 .

But just passing item1 to code

var infoEnum = valorEnum.GetType().GetMember(propriedade.GetValue(objeto, null).ToString()).FirstOrDefault();

It returns the class and the property it belongs to.

    
asked by anonymous 23.09.2016 / 01:04

2 answers

10

The ideal to explain it would be interesting to have a deeper understanding of how computers work, compilers, executables and other fundamentals of computing, but I try not to be too technical, I run the risk of being a bit vague. >

Metadata

The .Net was created to be rich in information for the programmer. When the compiler creates a code it adds a series of metadata about that code. It's like a database about your own code.

Then there is a data structure with a wealth of information about all the data types in your application (the types you have created or the types of libraries you use). These structures generally have informational and indicative texts of the relationship of each data with other data.

You can put custom information in this metadata. Have examples of how they work and the information available in the metadata in another question already asked . You can even create your own custom attributes .

Reflection

The reflection system is a set of algorithms to retrieve this information that is there next to your code. These are the methods you are using. If you are interested in learning more details you already have a question about .

The first step is GetType() which is a method available in all .Net objects. It will get the object passed to it, in case the example is valorEnum . This object has a pointer to its type (in simplicity, the exact way can vary). So instead of this pointer has the basic structure of the type and where are the metadata of it. This metadata is organized into a complex object called Type (you can see all his details in the documentation). There is a data tree about the type. There are several different information.

An enumeration has some members. Somewhere in this object is stored what are the members. The compiler put everything I needed there. The method GetMembers() takes all members of the type. Everyone even, even some that is not what you want. Of course you can filter this.

If you want a specific one you can call the method GetMember() . In case you need to pass some information that identifies this member. Something in propriedade helps define this. You are using the GetValue() method to get the desired object.

The return of GetMember() will give all required information in an array of objects MemberInfo . The first of these items is the one you want and the LINQ method FirstOrDefault() makes it the end result.

Example

I made a code that gets the data you want in a simple way (I used the same simple code I passed in previous answer and you did not like , this time I did step by step). See dotNetFiddle .

    
23.09.2016 / 02:41
5

Before starting to respond, it's important to explain what this code that you put in the question is for. I put together a Fiddle to explain how it converts an object to XML .

We declare a class:

public class TesteClasse 
{
    public TipoDoAmbiente TipoDoAmbiente { get; set; }
}

We instantiate the class with a value of TipoDoAmbiente :

var classe = new TesteClasse { TipoDoAmbiente = TipoDoAmbiente.Item2 };

We also instantiated a XML serializer and a special object, XmlWriter , which receives the conversion of an object into XML by the serializer. To write the conversion result, this XmlWriter needs a specialized streaming object (written in some type of memory, such as memory or disk, for example). For example, I've used StringWriter :

var xmlSerializer = new XmlSerializer(typeof(TesteClasse));
var stringWriter = new StringWriter();
var xmlWriter = XmlWriter.Create(stringWriter);

Recapping:

  • Convert a C # object to XML;
  • Write the object to another object that formats the XML;
  • Convert XML to string XML.
  • The result of XML is:

    <?xml version="1.0" encoding="utf-16"?>
    <TesteClasse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <TipoDoAmbiente>2</TipoDoAmbiente>
    </TesteClasse>
    

    Notice that I set TipoAmbiente to Tipo2 , but the XML serializer wrote only as 2 , according to its attribute definition.

    I'll do the reverse now. It starts with a string XML and converts it to the object, but this time using not a XmlWriter , but a XmlReader " and, similarly, a StringReader ( Fiddle here )

        var xml = "<?xml version=\"1.0\" encoding=\"utf-16\"?><TesteClasse xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><TipoDoAmbiente>2</TipoDoAmbiente></TesteClasse>";
        var xmlSerializer = new XmlSerializer(typeof(TesteClasse));
        TesteClasse testeClasse = (TesteClasse)xmlSerializer.Deserialize(XmlReader.Create(new StringReader(xml)));
        Console.WriteLine(testeClasse.TipoDoAmbiente);
    

    The example will write to console Item2 .

    Now, to your questions.

      

    The propriedade.GetValue(objeto, null); method returns the enum value, in case of this test I made the value is item1 . When I do a debug and stop at itemEnum nothing appears other than the value itself.

         

    I get this value and move on to the next methods, my question is how does the rest of the code "discover" the type and information of the member if I am just passing the value item1 ?

    If you also want to pass on the type and information of the member, it is not just getting the value from him that this is going to happen. You also need to retrieve the object PropertyInfo . related to the property you want to recover the value. This you have already done here:

    foreach (PropertyInfo propriedade in tipo.GetRuntimeProperties()) { ... }
    
      

    But only by passing the value item1 to the code var infoEnum = valorEnum.GetType().GetMember(propriedade.GetValue(objeto, null).ToString()).FirstOrDefault();

         

    It returns the class and the property it belongs to.

    Well, this is quite wrong. I think what you want is to retrieve an object by the value of TipoDeAmbiente of it, so I understood.

    In this case, System.Linq best resolves the situation for you:

    // Estou supondo que você desserializou um XML em uma lista, tipo List<TesteClasse>. 
    var propertiesTipoDeAmbiente = typeof(TesteClasse).GetProperties().Where(p => p.PropertyType == TipoDeAmbiente);
    foreach (var propriedade in propertiesTipoDeAmbiente)
    {
        foreach (var elemento in lista)
        {
            if (((TipoDeAmbiente)propriedade.GetValue(elemento, null)) == TipoDeAmbiente.Item1)
                yield return elemento; // Isto aqui acumula tudo em um IEnumerable<TesteClasse>. 
        }
    }
    

    Last thing

    This is very slow. Use this NuGet package to read this with decent performance.

        
    23.09.2016 / 02:53