我应该使用什么来自定义解析以提供可扩展性而不必继承?换句话说,我正在寻找用于解析自定义对象的IFormatProvider(用于输出)。
与以下代码相反:
var str = String.Format(new MyFormatProvider(), "{0:custom}", obj);
其中MyFormatProvider
实现IFormatProvider
答案 0 :(得分:1)
Open/Closed基本上只是说新的或更改的行为应该是可添加的,而无需修改现有的代码。在OOP中,通常使用继承或接口(一种特殊的"继承")来完成。
您需要对问题域更具体一点,以获得更具体的答案,但作为使用接口的示例如下:
interface IObjectParser {
object Parse(object obj, propertyName string);
}
class ReflectionParser : IObjectParser {
public object Parse(object obj, propertyName string) {
return obj.GetType().GetProperty(propertyName).GetMethod().Invoke(obj);
}
}
object parsedValue = new ReflectionParser().Parse(new MyClass(), "MyProperty");
然后,如果我们想添加一种新类型的解析器:
class DatabaseParser : IObjectParser {
public object Parse(object obj, propertyName string) {
return ExecuteQuery(
--Note the potential for SQL injection
string.Format("SELECT {1} FROM {0} WHERE Id = @id", obj.GetType().Name, propertyName),
((dynamic)o).Id
);
}
}
object parsedValue = new DatabaseParser(new MyClass(), "columnName");
这并非特别适合打开/关闭,但自从你提起IFormatProvider
以来,它使用另一种技术在格式字符串中挖掘可变数量的参数,同时仍然坚持强类型。
我不确定它是否有规范名称,但我将其称为"字符串输入" (你可以对一组对象做同样的事情 - 但是使用string
是相当普遍的并且是一个很好的双关语。您可以在JavaScript window.open windowFeatures和MVC' HtmlHelpers htmlAttributes(使用匿名类型达到相同效果)之类的内容中看到此类型的API,我会说& #39;在大多数情况下,它实际上是一个反模式,因为它打破了Liskov替换原则,但确实有它的利基用途。
IFormatProvider
(从技术上讲,ICustomFormatter
执行此部分,IFormatProvider
是工厂)必须支持未知数量的可能格式。为此,它会在"之后引导任何自定义格式化指令:" - 它预计适当的ICustomFormatter
将知道如何处理这些值。
IObjectParser
案例中的例子如下:
class ByteArrayParser : IObjectParser {
public object Parse(object obj, propertyName string) {
var bytes = obj as byte[];
// we've tunneled multiple parameters in the propertyName string
var nameParameters = propertyName.Split(":");
// note we've lost our strong typing, and coupled ourselves to the propertyName format
int index = int.Parse(nameParameters[0]);
string readAsType = nameParameters[1];
using (var ms = new MemoryStream(bytes))
using (var br = new BinaryReader(ms))
{
ms.Position = index;
switch (readAsType) {
case "float":
return br.ReadSingle();
case "int":
return br.ReadInt32();
case "string"
// we can even have yet another parameter only in special cases
if (nameParameters.Length > 2) {
// it's an ASCII string
int stringLength = int.Parse(nameParameters[2]);
return Encoding.ASCII.GetString(br.ReadBytes(stringLength));
} else {
// it's BinaryReader's native length-prefixed string
return br.ReadString();
}
default:
// we don't know that type
throw new ArgumentOutOfRangeException("type");
}
}
}
}
使用此课程时,您必须以只能通过文档发现的方式(propertyName
)专门格式化String.Format
。
// note again, we have to know ByteArrayParser's specific format and lose strong typing
object parsedIntValue = new ByteArrayParser().Parse(myBytes, "4:int");
object parsedSingleValue = new ByteArrayParser().Parse(myBytes, "4:float");
object parsedStringValue = new ByteArrayParser().Parse(myBytes, "4:string");
object parsedAsciiStringValue = new ByteArrayParser().Parse(myBytes, "4:string:15");