.NET Assemblies: Understanding Type Visibility

I am trying to reproduce something that System.Xml.Serialization is already doing, but for a different data source. At the moment, the task is limited to deserialization. That is, a given specific data source that I know how to read. Write a library that takes a random type, finds out fields / properties through reflection, then generates and compiles a "reader" class that can use a data source and an instance of this random type and write from the data source to the fields / properties of the object.

here is a simplified excerpt from my ReflectionHelper class

public class ReflectionHelper
{
    public abstract class FieldReader<T> 
    {
        public abstract void Fill(T entity, XDataReader reader);
    }

    public static FieldReader<T> GetFieldReader<T>()
    {
        Type t = typeof(T);
        string className = GetCSharpName(t);
        string readerClassName = Regex.Replace(className, @"\W+", "_") + "_FieldReader";
        string source = GetFieldReaderCode(t.Namespace, className, readerClassName, fields);

        CompilerParameters prms = new CompilerParameters();
        prms.GenerateInMemory = true;
        prms.ReferencedAssemblies.Add("System.Data.dll");
        prms.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().GetModules(false)[0].FullyQualifiedName);
        prms.ReferencedAssemblies.Add(t.Module.FullyQualifiedName);

        CompilerResults compiled = new CSharpCodeProvider().CompileAssemblyFromSource(prms, new string[] {source});

        if (compiled.Errors.Count > 0)
        {
            StringWriter w = new StringWriter();
            w.WriteLine("Error(s) compiling {0}:", readerClassName);
            foreach (CompilerError e in compiled.Errors)
                w.WriteLine("{0}: {1}", e.Line, e.ErrorText);
            w.WriteLine();
            w.WriteLine("Generated code:");
            w.WriteLine(source);
            throw new Exception(w.GetStringBuilder().ToString());
        }

        return (FieldReader<T>)compiled.CompiledAssembly.CreateInstance(readerClassName);
    }

    private static string GetFieldReaderCode(string ns, string className, string readerClassName, IEnumerable<EntityField> fields)
    {
        StringWriter w = new StringWriter();

        // write out field setters here

        return @"
using System;
using System.Data;

namespace " + ns + @".Generated
{
    public class " + readerClassName + @" : ReflectionHelper.FieldReader<" + className + @">
    {
        public void Fill(" + className + @" e, XDataReader reader)
        {
" + w.GetStringBuilder().ToString() + @"
        }
    }
}
";
    }
}

and calling code:

class Program
{
    static void Main(string[] args)
    {
        ReflectionHelper.GetFieldReader<Foo>();
        Console.ReadKey(true);
    }

    private class Foo
    {
        public string Field1 = null;
        public int? Field2 = null;
    }
}

, Foo . ! XML XML - - : ? System.Xml.Serialization Reflector , - , ...

, / , , , !

+3
3

sgen.exe( XML), :

Warning: Ignoring 'TestApp.Program'.
  - TestApp.Program is inaccessible due to its protection level. Only public types can be processed.
Warning: Ignoring 'TestApp.Program+Foo'.
  - TestApp.Program+Foo is inaccessible due to its protection level. Only public types can be processed.
Assembly 'c:\...\TestApp\bin\debug\TestApp.exe' does not contain any types that can be serialized using XmlSerializer.

new XmlSerializer(typeof(Foo)) :

System.InvalidOperationException: TestApp.Program+Foo is inaccessible due to its protection level. Only public types can be processed.

, , XmlSerializer ?

, . . , .NET.

, IL , DynamicMethod, skipVisibility = true, - /.

0

. XmlSerializer - API Reflection, :

GetFields() :

foreach (var field in myType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
    // ...

Im, BindingFlags, , , .

SetValue() . FieldInfo ( GetFields() ) , , :

field.SetValue(myObject, myValue);

myObject.Field = myValue;, , .

. , , .

public static T Deserialize<T>(XDataReader dataReader) where T : new()
{
    return (T) deserialize(typeof(T), dataReader);
}
private static object deserialize(Type t, XDataReader dataReader)
{
    // Handle the basic, built-in types
    if (t == typeof(string))
        return dataReader.ReadString();
    // etc. for int and all the basic types

    // Looks like the type t is not built-in, so assume it’s a class.
    // Create an instance of the class
    object result = Activator.CreateInstance(t);

    // Iterate through the fields and recursively deserialize each
    foreach (var field in t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
        field.SetValue(result, deserialize(field.FieldType, dataReader));

    return result;
}

, XDataReader, , ​​. , , .

, , ( int? ), , :

Foo myFoo = Deserialize<Foo>(myDataReader);

, Foo , .

+1

. , , , . Serialization DeSerealization , . ( ), , . , .Net , ID ( , - ).

, , , , , , , . , Foo FieldReader , :

namespace FooLibrary
{    
    public class Foo
    {
        public string Field1 = null;
        public int? Field2 = null;
    }

    public abstract class FieldReader<T>
    {
        public abstract void Fill(T entity, IDataReader reader);
    }    
}

(using FooLibrary;)

, . , , GetCSharpName ( t.Name) XDataReader, IDataReader ( , ), EntityField

public class ReflectionHelper
{
    public static FieldReader<T> GetFieldReader<T>()
    {
        Type t = typeof(T);
        string className = t.Name;
        string readerClassName = Regex.Replace(className, @"\W+", "_") + "_FieldReader";
        object[] fields = new object[10];
        string source = GetFieldReaderCode(t.Namespace, className, readerClassName, fields);

        CompilerParameters prms = new CompilerParameters();
        prms.GenerateInMemory = true;
        prms.ReferencedAssemblies.Add("System.Data.dll");
        prms.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().GetModules(false)[0].FullyQualifiedName);
        prms.ReferencedAssemblies.Add(t.Module.FullyQualifiedName);
        prms.ReferencedAssemblies.Add("FooLibrary1.dll");

        CompilerResults compiled = new CSharpCodeProvider().CompileAssemblyFromSource(prms, new string[] { source });

        if (compiled.Errors.Count > 0)
        {
            StringWriter w = new StringWriter();
            w.WriteLine("Error(s) compiling {0}:", readerClassName);
            foreach (CompilerError e in compiled.Errors)
                w.WriteLine("{0}: {1}", e.Line, e.ErrorText);
            w.WriteLine();
            w.WriteLine("Generated code:");
            w.WriteLine(source);
            throw new Exception(w.GetStringBuilder().ToString());
        }

        return (FieldReader<T>)compiled.CompiledAssembly.CreateInstance(readerClassName);
    }

    private static string GetFieldReaderCode(string ns, string className, string readerClassName, IEnumerable<object> fields)
    {
        StringWriter w = new StringWriter();

        // write out field setters here

        return @"   
using System;   
using System.Data;   
namespace " + ns + ".Generated   
{    
   public class " + readerClassName + @" : FieldReader<" + className + @">    
   {        
         public override void Fill(" + className + @" e, IDataReader reader)          
         " + w.GetStringBuilder().ToString() +         
   }    
  }";        
 } 
}

By the way, I found a tiny error, you have to use the new or override the Fill method, since it is abstract.

Well, I have to admit that GetFieldReader returns null, but at least the compiler compiles it.

Hope this helps you or at least leads you to a good answer

0
source

Source: https://habr.com/ru/post/1758563/


All Articles