C# 使用静态类或声明类

C# 使用静态类或声明类,c#,class,static,definition,C#,Class,Static,Definition,我目前正在尝试使用BinaryReader读取一些二进制数据。我已经创建了一个助手类来解析这些数据。目前它是一个静态类,使用这种方法: public static class Parser { public static ParseObject1 ReadObject1(BinaryReader reader){...} public static ParseObject2 ReadObject2(BinaryReader reader{...} } 然后我就这样使用它:

我目前正在尝试使用BinaryReader读取一些二进制数据。我已经创建了一个助手类来解析这些数据。目前它是一个静态类,使用这种方法:

public static class Parser
{
     public static ParseObject1 ReadObject1(BinaryReader reader){...}
     public static ParseObject2 ReadObject2(BinaryReader reader{...}
}
然后我就这样使用它:

...
BinaryReader br = new BinaryReader(@"file.ext");
ParseObject1 po1 = Parser.ReadObject1(br);
...
ParseObject1 po2 = Parser.ReadObject2(br);
...
但是我开始思考,我也可以像这样初始化这个类

Parser p = new Parser(br);
ParseObject1 po1 = Parser.ReadObject1();

什么是更好的实现。

两种实现之间的性能差异可能可以忽略不计。我预计读取二进制文件将花费99%以上的执行时间


如果您真正关心性能,您可以将两个实现包装在单独的循环中,并对它们计时。

哪一个更快在这里并不重要;您关心的更多的是并发性和体系结构

对于将BinaryReader作为参数传递给ReadObject调用的静态解析器类,您向该方法提供了所有数据,并且(根据您的示例,可能是这样)没有在解析器中持久化任何关于读取器的数据;这允许您实例化多个BinaryReader对象,并分别对其调用解析器,而不会出现并发或冲突问题。(请注意,这仅适用于解析器对象中没有持久静态数据的情况。)

另一方面,如果解析器获得要操作的BinaryReader对象,那么它可能会将BinaryReader数据保存在其自身中;如果使用不同的BinaryReader对象对解析器进行交叉调用,则可能会有一个复杂的问题


如果您的解析器不需要维护ReadObject1和ReadObject2之间的状态,我建议将其保持为静态,并传入BinaryReader对象引用;在那个实例中保持它的静态是一个很好的“描述符”,说明在这些调用之间并没有持久化的数据。另一方面,如果解析器中有关于BinaryReader的持久化数据,我会使其成为非静态的,并传入数据(如第二个示例中所示)。将其设置为非静态但使用类持久化数据,这样就不太可能导致并发问题。

这两种方法之间的性能差异应该可以忽略不计。就个人而言,我建议使用非静态方法,因为它提供了灵活性。如果您发现将许多解析逻辑整合到一个地方很有帮助,那么可以使用组合方法(在下面的示例中演示)

关于性能,如果您在短时间内重复创建解析器类的许多新实例,您可能会注意到性能影响很小,但是您可能能够重构代码以避免重复创建解析器类的实例。此外,虽然调用实例方法(特别是虚拟方法)在技术上不如调用静态方法快,但性能差异应该可以忽略不计

McWafflestix提出了一个关于国家的好观点。但是,考虑到您当前的实现使用静态方法,我假设您的解析器类不需要在对读取方法的调用之间保持状态,因此您应该能够重用相同的解析器实例,以便从
BinaryReader
流解析多个对象

下面的例子说明了我可能会采取的解决这个问题的方法。以下是此示例的一些功能:

  • 使用多态性来抽象关于给定对象类型的解析逻辑所在位置的详细信息
  • 使用存储库来存储解析器实例,以便可以重用它们
  • 使用反射来标识给定类或结构的解析逻辑
注意,我将解析逻辑保存在
ParseHelper
类中的静态方法中,而
myobjectapaser
MyObjectBParser
类上的
Read
实例方法使用
ParseHelper
类上的静态方法。这只是一个设计决策,您可以根据对如何组织解析逻辑最有意义的内容来做出决定。我猜想将一些特定于类型的解析逻辑移到各个解析器类中可能是有意义的,但将一些常规解析逻辑保留在ParseHelper类中

// define a non-generic parser interface so that we can refer to all types of parsers
public interface IParser
{
    object Read(BinaryReader reader);
}

// define a generic parser interface so that we can specify a Read method specific to a particular type
public interface IParser<T> : IParser
{
    new T Read(BinaryReader reader);
}

public abstract class Parser<T> : IParser<T>
{
    public abstract T Read(BinaryReader reader);

    object IParser.Read(BinaryReader reader)
    {
        return this.Read(reader);
    }
}

// define a Parser attribute so that we can easily determine the correct parser for a given type
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
public class ParserAttribute : Attribute
{
    public Type ParserType { get; private set; }

    public ParserAttribute(Type parserType)
    {
        if (!typeof(IParser).IsAssignableFrom(parserType))
            throw new ArgumentException(string.Format("The type [{0}] does not implement the IParser interface.", parserType.Name), "parserType");

        this.ParserType = parserType;
    }

    public ParserAttribute(Type parserType, Type targetType)
    {
        // check that the type represented by parserType implements the IParser interface
        if (!typeof(IParser).IsAssignableFrom(parserType))
            throw new ArgumentException(string.Format("The type [{0}] does not implement the IParser interface.", parserType.Name), "parserType");

        // check that the type represented by parserType implements the IParser<T> interface, where T is the type specified by targetType
        if (!typeof(IParser<>).MakeGenericType(targetType).IsAssignableFrom(parserType))
            throw new ArgumentException(string.Format("The type [{0}] does not implement the IParser<{1}> interface.", parserType.Name, targetType.Name), "parserType");

        this.ParserType = parserType;
    }
}

// let's define a couple of example classes for parsing

// the MyObjectA class corresponds to ParseObject1 in the original question
[Parser(typeof(MyObjectAParser))] // the parser type for MyObjectA is MyObjectAParser
class MyObjectA
{
    // ...
}

// the MyObjectB class corresponds to ParseObject2 in the original question
[Parser(typeof(MyObjectAParser))] // the parser type for MyObjectB is MyObjectBParser
class MyObjectB
{
    // ...
}

// a static class that contains helper functions to handle parsing logic
static class ParseHelper
{
    public static MyObjectA ReadObjectA(BinaryReader reader)
    {
        // <code here to parse MyObjectA from BinaryReader>
        throw new NotImplementedException();
    }

    public static MyObjectB ReadObjectB(BinaryReader reader)
    {
        // <code here to parse MyObjectB from BinaryReader>
        throw new NotImplementedException();
    }
}

// a parser class that parses objects of type MyObjectA from a BinaryReader
class MyObjectAParser : Parser<MyObjectA>
{
    public override MyObjectA Read(BinaryReader reader)
    {
        return ParseHelper.ReadObjectA(reader);
    }
}

// a parser class that parses objects of type MyObjectB from a BinaryReader
class MyObjectBParser : Parser<MyObjectB>
{
    public override MyObjectB Read(BinaryReader reader)
    {
        return ParseHelper.ReadObjectB(reader);
    }
}

// define a ParserRepository to encapsulate the logic for finding the correct parser for a given type
public class ParserRepository
{
    private Dictionary<Type, IParser> _Parsers = new Dictionary<Type, IParser>();

    public IParser<T> GetParser<T>()
    {
        // attempt to look up the correct parser for type T from the dictionary
        Type targetType = typeof(T);
        IParser parser;
        if (!this._Parsers.TryGetValue(targetType, out parser))
        {
            // no parser was found, so check the target type for a Parser attribute
            object[] attributes = targetType.GetCustomAttributes(typeof(ParserAttribute), true);
            if (attributes != null && attributes.Length > 0)
            {
                ParserAttribute parserAttribute = (ParserAttribute)attributes[0];

                // create an instance of the identified parser
                parser = (IParser<T>)Activator.CreateInstance(parserAttribute.ParserType);
                // and add it to the dictionary
                this._Parsers.Add(targetType, parser);
            }
            else
            {
                throw new InvalidOperationException(string.Format("Unable to find a parser for the type [{0}].", targetType.Name));
            }
        }
        return (IParser<T>)parser;
    }

    // this method can be used to set up parsers without the use of the Parser attribute
    public void RegisterParser<T>(IParser<T> parser)
    {
        this._Parsers[typeof(T)] = parser;
    }
}
//定义一个非泛型解析器接口,以便我们可以引用所有类型的解析器
公共接口IParser
{
对象读取(二进制读取器);
}
//定义泛型解析器接口,以便我们可以指定特定于特定类型的读取方法
公共接口IParser:IParser
{
新的T读取器(二进制读取器);
}
公共抽象类解析器:IParser
{
公共摘要T-Read(二进制阅读器);
对象IParser.Read(二进制读取器)
{
返回此。读取(读卡器);
}
}
//定义一个解析器属性,以便我们可以轻松地为给定类型确定正确的解析器
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct,AllowMultiple=false,Inherited=true)]
公共类ParserAttribute:属性
{
公共类型ParserType{get;private set;}
公共ParserAttribute(类型parserType)
{
if(!typeof(IParser).IsAssignableFrom(parserType))
抛出新ArgumentException(string.Format(“类型[{0}]未实现IParser接口。”,parserType.Name),“parserType”);
this.ParserType=ParserType;
}
公共ParserAttribute(类型parserType,类型targetType)
{
//检查parserType表示的类型是否实现了IParser接口
if(!typeof(IParser).IsAssignableFrom(parserType))
抛出新ArgumentException(string.Format(“类型[{0}]未实现IParser接口。”,parserType.Name),“parserType”);
//检查parserType表示的类型是否实现了IParser接口,其中T是指定的类型
        ParserRepository parserRepository = new ParserRepository();

        // ...

        IParser<MyObjectA> parserForMyObjectA = parserRepository.GetParser<MyObjectA>();
        IParser<MyObjectB> parserForMyObjectB = parserRepository.GetParser<MyObjectB>();

        using (var fs = new FileStream(@"file.ext", FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            BinaryReader br = new BinaryReader(fs);

            MyObjectA objA = parserForMyObjectA.Read(br);
            MyObjectB objB = parserForMyObjectB.Read(br);

            // ...
        }

        // Notice that this code does not explicitly reference the MyObjectAParser or MyObjectBParser classes.