6

我有一个包含一堆属性的类。如果程序员在该类型的对象上调用 ToString() 是错误的。以这个示例代码为例:

using System;

public class Foo
{
    public int ID = 123;
    public string Name = "SomeName";

    private string ToString() { return null; }
}

public class MyClass
{
    public static void Main()
    {
        Foo myObj = new Foo();
        WL("I want this to be a compiler error: {0}", myObj.ToString());
        RL();
    }

    #region Helper methods

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);   
    }

    private static void RL()
    {
        Console.ReadLine(); 
    }

    #endregion
}

您可以推断,如果大多数人希望将 ID 写为字符串,那么我应该实现 ToString 以便它返回 ID。但是,我认为这是一种不好的做法,因为程序员会“意外”获得工作代码。使用我的类的程序员应该指定他们想要什么。

相反,我想要的是如果有人调用 myObj.ToString() 以使其显示为编译时错误。我想我可以通过创建一个私有的 ToString() 函数来做到这一点,但这不起作用。

我提出这个问题的原因是我们最终得到了一个包含完全限定类名而不是 ID 的查询字符串。

所以问题是:有没有办法“隐藏” ToString() 函数,以便在我的类的对象上调用它会导致编译器错误?

4

7 回答 7

45

我怎么强调这个设计是多么糟糕的想法都不为过。

ToString()是 .Net 中对象合同的一部分。如果您不想实现它,请不要覆盖它,而让它返回类型信息。这可能造成什么危害?

我不是故意这么消极,但我绝对是有人想要摆脱的ToString()

一些额外的点:

  1. 为什么使用这个类的程序员假定它ToString()会返回一个 ID?你的生态系统中的其他类是否也在这样做?可以说ToString()应该返回一些有意义的数据。但是你真的不应该针对ToString()调用的结果进行编程。 ToString()用于类的字符串表示,句点。这听起来像是程序员或部门之间的教育或沟通问题。

  2. 以任何方式削弱ToString(),无论您是否可以在编译时弄清楚如何在运行时抛出异常,都会产生涟漪。我从未见过这样做,也不希望我使用的任何课程都会表现出这种行为。我想大多数程序员都会有同样的期望。未来使用你的课程的程序员会期待这个吗?你在路上造成了哪些错误和维护噩梦?

  3. 这对依赖 IDE 或调试器有什么影响ToString()

  4. 当使用不绑定特定类型但在运行时使用反射提取值的数据绑定技术时,这会产生什么影响?ToString()如果未指定使用成员,大多数数据绑定将回退到调用对象。

于 2008-10-20T15:47:10.250 回答
20

Obsolete 属性允许您执行此操作。

[Obsolete("Use the XYZ properties instead of .ToString() on Foobar", true)]

最后的布尔值是编译器是否应该考虑使用这个成员的错误。

于 2008-10-20T15:42:35.470 回答
7

出于几个原因,我完全不同意使用 Obsolete 属性。

首先,您现在将收到一个警告,警告您覆盖并使用 Obsolete 属性标记的 ToString() 方法:

    [Obsolete("dont' use", true)]
    public override string ToString()
    {
        throw new Exception("don't use");
    }

产生此警告:警告 1 过时成员 'ClassLibrary1.Foo.ToString()' 覆盖非过时成员 'object.ToString()' d:\source\ClassLibrary1\ClassLibrary1\Class1.cs 11 32 ClassLibrary1

所以现在你在代码中遇到了永久警告。最重要的是,它并不能完全解决您的问题。当框架中的某些内容现在隐式调用 ToString() 时会发生什么?下面代码的结果是,ToString() 体中的代码仍然被调用:

        Foo myObj = new Foo();

        Console.WriteLine(myObj);

所以现在你的代码中有一个警告,它实际上并没有阻止开发人员重新做同样的事情。我认为正确的做法是尝试找到一种在运行时抛出适当异常的方法,而不是试图弄乱 .net 对象契约。

在编译时发现问题的建议:我意识到我以前没有提出过解决这个问题的建议。我真的不知道你的 id 是什么格式,所以我只是猜测它是一个 int,但为什么不用查询字符串保护任何创建 url 并将 id 作为 int 传递。这样,开发人员就不会在没有编译错误的情况下意外传入一些无意义的字符串。比如这样:

public string CreateItemUrl(int itemId)
{
   return string.Format("someurl.aspx?id={0}", itemId);
}

现在,调用这个:

CreateItemUrl(myObj.Id);

变得比以下强类型和更不容易出错:

string theUrl = string.Format("someurl.aspx?id={0}", myObj);
于 2008-10-20T16:11:46.240 回答
7

我会采用混合方法。(嘿,不是要梳理其他答案吗?:))

首先,创建一个返回 void 的新 ToString。没有返回值意味着他们不能使用它来获得任何意外的好代码:

public new void ToString() { }

接下来,添加 Obsolete 属性,这样当人们调用它时,他们会收到一个警告,告诉他们 ToString 是错误的。

您不需要以这种方式覆盖 ToString,只需用无用的东西隐藏它即可。它没有返回的事实将破坏所有代码,从而在过时的消息之上导致编译器错误。

如果我直接理解您的问题,则投射到 Object 的人不是您关心的问题。您不想阻止人们调用 ToString 并获取类型信息,而是要防止他们意外地认为 ToString 提供了有用的结果。

编辑:请不要抛出异常或覆盖 ToString。当您的对象被视为对象时,这将导致“坏事”。只需使用“新”即可获得您所要求的好处,而不会破坏其他框架。

于 2008-10-20T20:37:13.833 回答
1

使用带有公共 ToString() 函数的 override 关键字来覆盖 System.Object ToString() 方法。

于 2008-10-20T15:43:25.377 回答
1

覆盖 ToString 以返回 string.Empty,那么您将不会有任何附加到查询字符串。默认情况下,如果您不覆盖 ToString,您将获得返回 this.GetType() 的 Object 版本,这将为您提供命名空间和类名之类的信息。

调用 ToString 似乎是一件非常合理的事情,我不想为这样做的人引发编译器错误。

于 2008-10-20T15:52:09.320 回答
0

请考虑更改您的设计/意见:)

首先,Foo.ToString 的定义并没有为 Object.ToString() 定义一个覆盖,而是一个新的,并且应该以“new”关键字为前缀,以防止对语义的误解。或明确声明“覆盖”。恕我直言,编译器发出相应的警告。

即使你会找到一种方法来禁止调用 Foo.ToString,它也会在编译时被禁止,只有当“this”的类型已知是 Foo 或后代,但是 ((object) foo).ToString()将是一个正确的解决方法,因为 ToString 是 Object 接口的一种方法。

此外,阻止调用 ToString 是不可取的,因为 Debugger 使用它来呈现一个值。SY,杰克

于 2008-10-20T16:20:04.460 回答