我可以覆盖公共API中的类的ToString()吗?

时间:2010-11-18 01:45:09

标签: c# .net extension-methods tostring typeconverter

我正在编写一个与另一个软件的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有我的自定义扩展方法,那么应该使用它,否则代码应该回到普通的旧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();
        }
    }

上面的代码不起作用,因为扩展方法aren't found by GetMethod()

5 个答案:

答案 0 :(得分:2)

您可以为每个类提供一个包装器:

    public class SomeAPITypeWrapper : SomeAPIType
    {
        public override string ToString()
        {
            return SomeProperty;
        }
    }

    public class SomeOtherAPITypeWrapper : SomeOtherAPIType
    {
        public override string ToString()
        {
            return SomeOtherProperty;
        }
    }

这肯定允许在您的问题中使用基类/子类。它还使它保持干净并且在对象模型本身内,而不是在switch语句或帮助器类中。

答案 1 :(得分:0)

您是否尝试在扩展类中使用除ToString()之外的其他名称?我也不完全确定扩展方法,但我猜测base.ToString被调用而不是你的。可能使ToDescription()扩展方法产生更好的结果。

答案 2 :(得分:0)

如果可以通过实例方法和扩展方法解析给定的方法调用,则优先考虑实例方法。因此,需要对扩展方法进行命名,使其与扩展类型中的方法名称不同。

从上面的代码中,您似乎无法控制APIObject的来源及其派生。因此,您的选项为“Introduce Foreign Method”和“Introduce Local Extension

  • 我会尝试外来方法(类似于C#扩展方法)..不知道为什么你需要反射。如果扩展方法不存在,则它是编译时错误。你是如何使用这种方法的?
  • 最后,切换语句并不是那么糟糕......除非它们很长/需要经常更改/跨位置重复。

答案 3 :(得分:0)

我建议制作Dictionary<Type,Converter<object,string>>。然后,您可以查找自定义字符串,如果找不到,请调用ToString

注意,字典将检查类型的完全匹配,因此如果要处理子类,则必须编写一些额外的代码以查看字典中是否列出了基本类型(提示:如果匹配基础class,或者即使你不这样做,继续将实际的派生类型添加到字典中,这样你就不必再通过继承树进行递归了。)

请注意,您可以为符合Object.ToString()合同的Converter<object,string>构建一个“开放委托”,并将其作为默认值使用,甚至将其存储在字典中,而不是将其置于特殊情况下。到ToString

答案 4 :(得分:0)

您可以将所有tostringing推迟到您的应用程序的单独关注。 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,您可以定义字符串中包含哪些字段。