继承类的 C# 静态构造函数

问题描述 投票:0回答:1

我有一个包含大约 100k XML 配置文件的项目,用于管理测试设备。为了使文件更容易更新,我希望能够将它们移植到 Excel、Access 等。我已经编写了 VBA 代码来执行此操作,但速度非常慢。 C# 的速度要快几个数量级,并且提供更多实用程序。我创建了 XML 架构来读取 XML,但现在我想创建一组可重用的数据对象来管理对各种格式的读写以及指定数据内容的派生对象。

我决定构建一个两层数据结构,但静态构造函数似乎没有按预期执行,我需要一种解决方法。

public abstract class Data_Structure {
    private static readonly Dictionary<string, int> _DatIndex = new Dictionary<string, int>();
    private static string _Name = "";
    private string[] _Data = null;
    protected static void Init(string datName, List<string> listProps) {
        int iIndex = 1;
        _Name = datName;
        foreach (string iProp in listProps) {
            _DatIndex.Add(iProp, iIndex);
            iIndex++;
        }
    }
    public Data_Structure() {
        _Data = new string[_DatIndex.Count + 1];
    }
    public static Dictionary<string, int> Get_PropList => _DatIndex;
    public static string Get_Name => _Name; 
    public string Prop_Get(string Prop_Name) {
        return this._Data[_DatIndex[Prop_Name]];
    }
    public void Prop_Set(string Prop_Name, string Prop_Value) {
        this._Data[_DatIndex[Prop_Name]] = Prop_Value);
    }
    // Code to manage input/output
}
public class Data_Item : Data_Structure {
    static Data_Item() {
        List<string> listProps = new List<string>();
        PropertyInfo[] arryProps = typeof(Data_Item).GetProperties();
        foreach (PropertyInfo iProp in arryProps) {
            listProps.Add(iProp.Name);
        }
        Init("Data_Item_Name", listProps);
    }
    public string Property1 {
        get => this.Prop_Get("Property1");
        set => this.Prop_Set("Property1", value);
    }
    // More Properties...
}

这个想法是

Data_Structure
可以成为所有不同I/O格式之间的接口。对于每个新的 XML 文件类型,我将创建一个定义其属性的子实例(即
Data_Item
)。我想这样做,因为我可以使用诸如
BindingList<T>
等之类的东西

当我使用代码时,我尝试像这样提取属性列表:

public void testFunction() {
    Console.WriteLine(Data_Item.Get_PropList.Count); //Outputs 0
    Data_Item tempItem = new Data_Item();
    Console.WriteLine(Data_Item.Get_PropList.Count); //Outputs correct number of properties
}

我对反射不太熟悉,但我知道它在执行过程中非常慢。在上面的代码中,目的是预先加载反射部分,以便在运行时(迭代 100k 个文件时)执行得更快。此外,任何使派生类更易于定义的建议将不胜感激。谢谢!


编辑解决方案:

我发现,如果将静态属性放在父类中并访问它,则只会调用父类的静态构造函数。我找到了一个解决方法Here加上从所选答案中添加了一些代码:

public abstract class Data_Item<TChild> where TChild : Data_Item<TChild> {
    private static Data_Schema _CurSchema;
    static Data_Item() {
        _CurSchema = Data_Schema_Libarary.Get(typeof(TChild));
    }
}
public class Data_Item_Instance : Data_Item<Data_Item_Instance> {
    [Custom_Property(Attributes1)]
    public string Property1 {get; set;}
    [Custom_Property(Attributes2)]
    public string Property2 {get; set;}
}
public static class Data_Schema_Library {
    private static Dictionary<string, Data_Schema> _SchemaLibrary;
    public static Data_Schema Get<T>() {
        //Read T with Reflection and load a Data_Schema into/from _SchemaLibrary
    }
}
public class Data_Schema {
    public Custom_Property Attribute {get; set;}
}

我认为它仍然很慢,但它有所帮助。希望它也对其他人有帮助。

c# reflection static system.reflection static-constructor
1个回答
0
投票

“但是静态构造函数似乎没有按预期执行,我需要一种解决方法。”

使用静态变量的方式是一种反模式。静态变量无论如何都不是私有的——它们是全局变量,应该这样对待。这就像在您的

Main()
函数中使用每个静态值放置一个变量 - 这对您的用例没有意义。

public abstract class Data_Structure {
    private static string _Name = "";
    protected static void Init(string datName) {
        _Name = datName;
    }
}
public class Data_Item : Data_Structure {
    static Data_Item() {
        Init("ChildClass");
    }
}
//  Program.cs
Assert(Data_Structure._Name == "");
new Data_Item();
Assert(Data_Structure._Name == "ChildClass");

您在一个示例中看到了这一点,并且您似乎对此感到困惑。这是因为您没有访问子类的值,您正在访问父类的值。

这个想法是 Data_Structure 可以成为所有不同 I/O 格式之间的接口

您可以执行此操作,但您不想使用静态成员执行此操作。理想情况下,C# 中的所有内容都是类的实例,而不是静态变量,因为这有助于划定责任界限。

建议是创建一个模式对象来处理每种数据类型的字符串到索引的映射。然后,您可以为每种类型创建一个架构实例,并将其用作该类型的所有映射的事实来源。这是一个最小的例子:

public interface IDataStructureSchema
{
    Dictionary<string, int> Items { get; }

    int Index(string index);
}

这个模式类可以完成您希望您的 schema 完成的所有操作——它索引数据并提供字符串到底层 int 类型的映射。你所要做的就是创建一个实现这个接口的全新类,然后你就可以在后端做任何你想做的事情了。

现在,要创建使用此架构的数据项类型,我们只需将其添加为字段并将其映射用于所有查找:

public class BaseItem
{
    public BaseItem(IDataStructureSchema schema, string[] data)
    {
        _schema = schema;
        _data = data;
    }

    protected IDataStructureSchema _schema;
    protected string[] _data;

    protected string Get(string index)
    {
        return _data[_schema.Index(index)]
    }

    protected void Set(string index, string value)
    {
        _data[_schema.Index(index)] = value;
    }
}

public class DataItem : BaseItem
{
    public BaseItem(IDataStructureSchema schema, string[] data)
        : base(schema, data)
    {
    }

    public string Property1 {
        get => this.Get("Property1");
        set => this.Set("Property1", value);
    }
}

现在您所需要的只是一点代码来为每种项目类型生成架构。您已经有了相当好的基础,所以这里有一个片段可以帮助您开始:

public static class SchemaCompiler
{
    private Dictionary<Type, IDataStructureSchema> _cache = new Dictionary<Type, IDataStructureSchema>();
    public static IDataStructureSchema Create(Type dataItem)
    {
        if (_cache.TryGetValue(dataItem, out var cachedSchema)
            return cachedSchema;

        //  There's no cached schema!
        //  Make one here using reflection
        //  and then add it to _cache when you're done.
    }
}

为什么要这么做?

所提供示例中的每个项目都有明确的责任界限。在您提供的示例中,一个类负责所有事情。现在,我们有三个可以独立操作的类:一个用于访问数据的类,一个用于定义数据如何从字符串映射到整数的类,以及一个创建从字符串到整数的映射的类。

我对反射不太熟悉,但我知道它在执行过程中非常慢。

在您分析代码并发现反射是罪魁祸首之前,您不应该考虑反射优化。您的 XML 解析器可能会承担大部分负载,因为它必须比已经经过大量调整的内置反射方法完成更多的工作。

即使反射比编译的 C# 慢 100 倍,它仍然可能比 XML/配置文件解析器慢 2 倍。首先看那里——然后看反思。 (警告:这是一般性陈述,并不总是正确的。首先分析您的代码!

如果您绝对确定需要比反射更快的东西,请查看表达式树,它允许您轻松地针对特定任务即时编译 C# 代码。 这应该是您情况下的最后手段。

© www.soinside.com 2019 - 2024. All rights reserved.