利用可选参数的边缘情况,通过接口滥用语言来强制实现ToString

本文关键字:语言 接口 ToString 实现 参数 边缘 情况 | 更新日期: 2023-09-27 18:12:10

我有一个接口IKey,我想有一个方法,它将返回键作为字符串。我们考虑过这样一个方法:

String GetAsString();

将返回字符串表示形式,但希望能够在接口中再次声明ToString()以强制实现者实现它,但它并没有强制他们这样做,因为他们继承了Object的实现。这是建议:

public interface IKey
{
    string ToString(string dummyParameter=null);
}

这强制在任何实现类中实现该方法,但由于可选参数工作的方式,调用者不需要为此提供值,并且您确保对对象上的ToString()方法的任何调用,无论是转换为接口IKey还是实现类将始终调用类实现而不是Object实现。

在实现中,我们可以忽略dummyParameter并返回我们想要的,因为知道调用ToString()总是会实际调用ToString(null)

现在我觉得这是不对的,但同时它也有一些很好的东西。它几乎与GetAsString()方法完全相同,因为它只能在IKey接口和派生类上调用,除了它看起来像我们想要使用的更自然的ToString()方法,并且我们能够在子类中强制实现。

已经说过,没有使用的虚拟参数感觉不对。

所以这很可怕吗?还是大?

这个问题适合SO还是应该由程序员来问?

public class Key :IKey 
    { 
        public string ToString(string abc = null) 
        { 
            return "100"; 
        } 
    }
Key key = new Key ();
Trace.WriteLine (key.ToString());
Trace.WriteLine (key.ToString(null));
Trace.WriteLine (key.ToString("ac"));
Trace.WriteLine (((object)key).ToString());
输出:

100
100
100
Blah.Tests.Key

利用可选参数的边缘情况,通过接口滥用语言来强制实现ToString

听起来您正在使用应该使用抽象类的接口。下面的类明确要求后代已经实现了ToString

abstract class X
{
    public abstract override string ToString();
}

从我的角度来看,这样的ToString()方法在自定义接口中有点混乱,因为自定义接口暴露了一个标准的和众所周知的名称ToString()的方法。

我更喜欢更直接和明显的东西,比如:

string KeyText { get; }

或方法

string ConvertKeyToString();

另一个答案已经建议抽象类,我认为这是最好的选择。

将带有默认参数的ToString添加到接口的想法在实际中并不太有效。当调用不带参数的ToString时,重载解析将找到不带参数的ToString(我必须说,这似乎很直观)。考虑这个程序的输出:

void Main()
{   
    Console.WriteLine(new Key().ToString());
}
public interface IKey 
{
    string ToString(string dummy = null);
}
class Key : IKey 
{   
    public string ToString(string dummy) 
    {
        return "myspecialKey";
    }
}

输出object.ToString()实现。因此,如果您被限制使用接口,我会将方法命名为ToString()以外的其他名称。

(IMO) - ToString()已经有一个非常明确的目的和意义。根据名称劫持它可能很方便,但是说它是必需的,你就重新利用了一些你不应该使用的东西。

答案是有你自己的单独的方法,最初的想法。否则意味着所有关于ToString()的。net文档都变成了"错误的"。

。Tag属性是许多UI控件上的对象。它可能是你想要在某种控件库中"标记"控件。仅仅因为名字合适,类型合适,并不意味着意思是一样的,你可以抓住它并重新使用它。

我还建议考虑更改接口名称;除非实现者实际上是关键?我的印象是它们只是"有键",或者有一些与它们相关的键。在这种情况下,IKeyed, IIndexed或其他可能更好。那么string Key { get; }就变得更有吸引力了。也许这只是名字的问题。

我认为这是滥用,因为你在这里暗示的问题:

…并且您要确保对对象的ToString()方法的任何调用,无论是转换为接口IKey还是实现类,都将始终调用类实现而不是对象实现。

考虑以下代码:

IKey someKey = ...;
string keyAsString = someKey.ToString();
object someKeyAsObject = (object)someKey;
string keyAsString2 = someKeyAsObject.ToString();

任何人看到这段代码都会认为keyAsStringkeyAsString2是相同的。然而,这些将调用不同的方法,这些方法可能具有不同的行为。唷!

可怕,如果我只能选一个词…更准确地说,如果我在野外遇到这种情况,我的反应是困惑和怀疑。

在设计API时,我尽量遵循一条最重要的规则:不要让开发人员感到惊讶。

这肯定会是一个惊喜。它排除了使用预期的输出和使用ToString()而不需要特别的努力。事实上,没有任何迹象表明需要特别的努力,这将是"惊喜"的一部分。ToString()的默认实现最终比我预期的更频繁地被使用。我将避免禁止或扭曲它的使用,除非我没有其他合理的方法来解决问题。

我不认为这会比一个已经不是object成员的命名良好的方法/属性更"自然"。

刚刚遇到这个。让接口继承iformatable怎么样?

http://msdn.microsoft.com/en-us/library/system.iformattable.aspx

我自己没有这样做,所以我可能是错的,但这似乎可以为您的接口使用ToString方法的格式参数。请注意,您仍然没有改变无参数的ToString,我同意其他人不应该在接口中这样做,而是使用具有可识别格式输入的ToString(string format, IFormatProvider provider),这基本上意味着"在它实现I的上下文中给我这个对象的描述"。显然,每个实现的类都需要对方法进行编码,但这样做是恰当的。