我目前正在尝试使用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();
什么是更好的实施。
答案 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
流。
下面是一个示例,说明了我可能会针对此问题采取的方法。以下是此示例的一些功能:
请注意,我已将解析逻辑保留在ParseHelper
类中的静态方法中,Read
和MyObjectAParser
类上的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.