使用静态类或声明的类

时间:2010-11-02 21:23:07

标签: c# class static definition

我目前正在尝试使用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();

什么是更好的实施。

3 个答案:

答案 0 :(得分:8)

哪个更快并不是真正相关的;您的关注点更多是关于并发性和体系结构。

对于将BinaryReader作为参数传递给ReadObject调用的静态Parser类,您将向该方法提供所有数据,并且(可能来自您的示例)不保留任何有关的数据解析器中的读者;这允许您实例化多个BinaryReader对象并分别调用它们的Parser,没有并发或冲突问题。 (请注意,只有在Parser对象中没有持久性静态数据时才适用。)

另一方面,如果您的Parser传递了BinaryReader对象以进行操作,那么它可能会在其自身内部持有BinaryReader数据;如果您使用不同的BinaryReader对象对Parser进行交错调用,那么可能存在复杂的问题。

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

答案 1 :(得分:1)

两种实现之间的性能差异可能微不足道。我希望读取二进制文件需要> 99%的执行时间。

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

答案 2 :(得分:1)

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

关于性能,如果您在短时间内反复创建Parser类的许多新实例,您可能会注意到性能影响很小,但是您可能会重构代码以避免重复创建实例解析器类。此外,虽然调用实例方法(尤其是虚方法)在技术上没有调用静态方法那么快,但性能差异应该是非常微不足道的。

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

下面是一个示例,说明了我可能会针对此问题采取的方法。以下是此示例的一些功能:

  • 使用多态来抽象解析逻辑驻留给定类型对象的位置的详细信息。
  • 使用存储库存储Parser实例,以便可以重复使用它们。
  • 使用反射来识别给定类或结构的解析逻辑。

请注意,我已将解析逻辑保留在ParseHelper类中的静态方法中,ReadMyObjectAParser类上的MyObjectBParser实例方法使用这些静态方法ParseHelper类上的方法。这只是一个设计决策,您可以根据对组织解析逻辑最有意义的内容做出决定。我猜测将一些特定于类型的解析逻辑移动到单个Parser类中可能是有意义的,但是在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;
    }
}

用法示例:

        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.