【问题标题】:Using a static class, or a declared one使用静态类或声明的类
【发布时间】:2011-05-04 04:34:49
【问题描述】:

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

什么是更好的实现方式。

【问题讨论】:

    标签: c# class static definition


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      哪个更快在这里并不重要;您关心的更多是并发性和架构。

      在静态 Parser 类的情况下,您将 Bi​​naryReader 作为参数传递给 ReadObject 调用,您将所有数据提供给该方法,并且(可能从您的示例中)不保存任何关于解析器中的阅读器;这允许您实例化多个 BinaryReader 对象并分别在它们上调用 Parser,而不会出现并发或冲突问题。 (请注意,这只适用于 Parser 对象中没有持久静态数据的情况。)

      另一方面,如果您的 Parser 被传递到 BinaryReader 对象以对其进行操作,那么它可能会将 BinaryReader 数据保存在其自身中;如果您使用不同的 BinaryReader 对象对 Parser 的交错调用,则存在潜在的复杂性。

      如果您的 Parser 不需要维护 ReadObject1 和 ReadObject2 之间的状态,我建议将其保持为静态,并传入 BinaryReader 对象引用;在这种情况下保持静态是一个很好的“描述符”,即在这些调用之间没有持久的数据。另一方面,如果在解析器中保留了有关 BinaryReader 的数据,我会将其设为非静态,并将数据传入(如您的第二个示例中所示)。使其非静态但使用类持久化数据可以大大降低并发问题的可能性。

      【讨论】:

      • 很好的答案,我很少遇到以这种方式思考的程序员。
      • 好的,确实很棒的答案。这让我很清楚。从来没有这样想过。更快确实并不总是最佳实现的定义。
      • 很高兴为您提供帮助!我发现这种分析问题的方式非常有用,而且我通过这种方式避免了一些令人讨厌的并发问题!
      • 目前,Parser 类永远不会出现并发问题。但是在我的用例中可能有一段时间我想读取 2 个不同的二进制文件。我会让它们保持静止,以避免出现问题。我真的很喜欢它的可读性。
      【解决方案3】:

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

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

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

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

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

      请注意,我将解析逻辑保存在 ParseHelper 类中的静态方法中,MyObjectAParserMyObjectBParser 类上的 Read 实例方法利用 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.
      

      【讨论】:

      • 哇 xD 这是一个很好的灵活解决方案。但是,对于我当前的用例来说,它似乎有相当多的开销。但是,我会一直收藏这个,因为它确实让我学到了一些东西,并让我对如何解决这个问题有了另一种见解。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-04
      • 1970-01-01
      • 2014-05-12
      • 2014-11-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多