[英]Can I override ToString() for classes in a public API?
我正在编写一个与另一软件的API接口的工具。 我的工具的一部分将需要生成有关通过API找到的各种对象的报告,并且我希望这些报告包含标识每个对象的简单字符串。 默认情况下,我计划使用ToString()为每个对象生成字符串。 但是,毫不奇怪,我发现此API中的默认ToString()实现不具有描述性。
最初,我考虑使用长的Switch语句执行类似以下代码的操作。 尽管这很可能会变得难以控制。
public string GetAPIObjectDescrition(object obj)
{
Type t = obj.GetType();
Switch(t)
{
Case typeof(SomeAPIType):
SomeAPIType x = (SomeAPIType)obj;
return x.SomeProperty;
Case typeof(SomeOtherAPIType):
SomeOtherAPITypex = (SomeOtherAPIType)obj;
return x.SomeOtherProperty;
default:
return x.ToString();
}
}
接下来,我尝试使用扩展方法(请参见下面的代码)。 CustomObjectDescription()可以按预期工作,但是当我尝试调用ToString()时,它仅返回默认的ToString()结果。 我以前从未使用过扩展方法,因此我可能完全以为这样的想法是根本不可能的。
我不能保证API中遇到的每个Type都将有一个CustomObjectDescription()扩展,因此,如果我采用这种方法,我将不得不每次都不得不使用反射来检查当前对象是否具有GetObjectDescription()扩展。 。 我想尽可能避免使用反射。
public static class APIObjectDescriptionExtensions
{
public static string ToString(this APIObject element)
{
return "ElementName = " + element.Name + " ElementID =" + element.Id.IntegerValue.ToString();
}
public static string CustomObjectDescription(this APIObject element)
{
return "ElementName = " + element.Name + " ElementID =" + element.Id.IntegerValue.ToString();
}
}
有人对我应该如何解决此问题有其他建议吗? 我更喜欢一种解决方案,其中每种API类型的代码彼此独立(没有巨大的Switch语句)。
另外,如果可能的话,我希望一种类型的描述字符串代码可以继承到子类型,除非这些类型具有自己的唯一描述字符串代码。
我认为可能会有更好的解决方案,包括创建自定义TypeConverters或覆盖/扩展System.Convert.ToString()?
更新资料
我认为下面的示例可能有助于阐明我正在尝试做的事情。 最终,我希望能够从此API中获取任意类,并在运行时才知道其Type,然后生成描述字符串。 如果Type具有我的自定义扩展方法,则应使用它,否则代码应退回到普通的旧ToString()上。
public static string GetDataDescription(object o)
{
//get the type of the input object
Type objectType = o.GetType();
//check to see if a description extension method is defined
System.Reflection.MethodInfo extensionMethod = objectType.GetMethod("MyDescriptionExtensionMethod");
if (extensionMethod != null)
{
//if a description extension method was found returt the result
return (string)extensionMethod.Invoke(o, new object[] { });
}
else
{
//otherwise just use ToString();
return o.ToString();
}
}
上面的这段代码不起作用,因为GetMethod()找不到扩展方法。
您可以为每个类提供一个包装,类似于以下内容:
public class SomeAPITypeWrapper : SomeAPIType
{
public override string ToString()
{
return SomeProperty;
}
}
public class SomeOtherAPITypeWrapper : SomeOtherAPIType
{
public override string ToString()
{
return SomeOtherProperty;
}
}
当然,这允许您在问题中使用基类/子类。 它还可以使它保持整洁,并保持在对象模型内部,而不是在switch语句或帮助器类中。
您可以将所有字符串拖延到应用程序的单独关注中。 StatePrinter( https://github.com/kbilsted/StatePrinter )是这样一种API,您可以在其中使用默认值或根据要打印的类型进行配置。
var car = new Car(new SteeringWheel(new FoamGrip("Plastic")));
car.Brand = "Toyota";
然后打印
StatePrinter printer = new StatePrinter();
Console.WriteLine(printer.PrintObject(car));
然后您得到以下输出
new Car() {
StereoAmplifiers = null
steeringWheel = new SteeringWheel()
{
Size = 3
Grip = new FoamGrip()
{
Material = ""Plastic""
}
Weight = 525
}
Brand = ""Toyota"" }
使用IValueConverter抽象,可以定义打印机的类型,使用FieldHarvester,可以定义字符串中要包括的字段。
您是否在扩展类中尝试使用ToString()以外的其他名称? 我也不完全知道扩展方法,但是我猜是base.ToString被调用了而不是您的。 可能采用ToDescription()扩展方法会产生更好的结果。
我建议制作一个Dictionary<Type,Converter<object,string>>
。 然后,您可以查找自定义的字符串生成器,如果找不到,则调用ToString
。
请注意,字典将检查类型是否完全匹配,因此,如果要处理子类,则必须编写一些其他代码以查看是否在字典中列出了基类型(提示:如果您匹配基类,或者即使您不这样做,也可以将实际的派生类型添加到字典中,这样就不必再次遍历继承树。
请注意,您可以为Object.ToString()
建立一个符合Converter<object,string>
协定的“开放委托”,并将其用作默认协定,甚至将其存储在字典中,而不是对ToString
进行特殊处理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.