c# - .NET 程序集 : understanding type visibility

标签 c# .net reflection assemblies

我正在尝试重现 System.Xml.Serialization 已经完成的操作,但用于不同的数据源。 目前任务仅限于反序列化。 IE。给定我知道如何阅读的定义数据源。编写一个采用随机类型的库,通过反射了解它的字段/属性,然后生成并编译“读取器”类,该类可以获取数据源和该随机类型的实例,并从数据源写入对象的字段/属性。

这是我的 ReflectionHelper 类的简化摘录

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() + @"
        }
    }
}
";
    }
}

和调用代码:

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 类在 Program 类之外是不可见的。但! .NET XML 反序列化器以某种方式解决了这个问题——问题是:如何解决? 在通过 Reflector 挖掘 System.Xml.Serialization 一个小时后,我开始接受我在这里缺乏某种基本知识并且不确定我在寻找什么...

此外,我完全有可能重新发明轮子和/或在错误的方向上挖掘,在这种情况下请务必说出来!

最佳答案

您不需要创建动态程序集和动态编译代码来反序列化对象。 XmlSerializer 也不这样做——它使用反射 API,特别是它使用以下简单概念:

从任何类型中检索字段集

Reflection 为此目的提供了 GetFields() 方法:

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

我在此处包含 BindingFlags 参数以确保它将包含非公共(public)字段,否则默认情况下它将仅返回公共(public)字段。

设置任意类型的字段值

Reflection 为此提供了函数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 是私有(private)类型,您也可以这样做,就像在您的示例中一样。

关于c# - .NET 程序集 : understanding type visibility,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3432281/

相关文章:

java - 反射返回带有空元素的集合

c# - 类型比较未返回预期结果

c# - 使用反射在 C# 中查找字段的指针

c# - Quartz.NET - 计划任务停止随机运行

javascript - 如何使用 webpack 包含整个 polyfill 文件夹?

c# - 在 .NET 4.0 中处理没有第三方库的 Zip 文件?

.net - Jenkins 中 .NET 应用程序的控制台输出到哪里?

c# - RichTextBox (WPF) 没有字符串属性 "Text"

c# - 在 ASP.NET C# 中反序列化 JSON

c# - 检查集合中的重复项