是否可以定义无法实现的有效C#接口?

Mar*_*han 8 c# interface

我已经考虑过几天这个(meta)问题:
是否有可能定义有效的C#接口,无法以任何方式实现?

这个问题的可能变化:是否可以在C#2.0,3.0,4.0,5.0中定义这样的接口?是否有可能定义这样的接口,在实现时甚至不编译,或者编译但抛出运行时异常?

编辑:我知道这样的界面只是根据定义是无用的,但对于编程工作的演讲或测试申请人来说,他们知道C#有多好,这是一个很好的答案.

Eri*_*ert 17

是否可以定义无法实现的有效C#接口?

这个琐事问题不适合StackOverflow,但是很简单,很容易回答.(错误的是,事实证明!继续阅读!)

class C
{
    private C() {}
}
interface IFoo<T> where T : C, new()
{
}
Run Code Online (Sandbox Code Playgroud)

IFoo<T>无法实现任何T因为没有可替换的类型参数T.C不起作用,因为C没有公共无参数构造函数,并且没有派生类,C因为默认构造函数是私有的.(嗯,有可能是一个接近的派生类CC,但没有在这种情况下).


更新:评论者"迈克z"正确地指出了这一点

class X<T> : IFoo<T> where T : C, new() {}
Run Code Online (Sandbox Code Playgroud)

实现接口,虽然当然现在没有办法实例化X<T>!

更好的是,用户"GranBurguesa"指出允许声明派生类C,只要它永远不会调用私有构造函数; 这只有在实例化时崩溃并死亡时才有可能.(好吧,要挑剔,也可以将递归调用优化到无限循环而不是崩溃.)

这两种狡猾的解决方法都提出了一个哲学问题:如果一个接口是由一个没有人可以实例化的类实现的,那么它是否真的实现了?当然GranBurguesa证明IFoo<D>可以实现和构建,所以我的答案实际上是错误的.


还有一些案例,例如在SLaks删除的答案中暗示的案例,其中滥用通用机制导致"无限"类型.这些类型在CLR中是不合法的; C#设计团队已经考虑过为C#编译器规范添加类似的语言,但尚未解决它.使用这些类型可能会使编译器或运行时崩溃.

有关崩溃编译器的infinitary类型的示例,请参阅我的文章:

无限但不是超越


这是一个.剪切n将此代码粘贴到Visual Studio中,您将看到此接口无法实现:

interface ?AmAPerfectlyOrdinaryInterface { }

class C : IAmAPerfectlyOrdinaryInterface { }

  • @InBetween:你的三个问题的答案是:(1)因为它是一个合法的程序,(2)是的,以及(3)编译器不需要捕捉开发人员所做的每一件蠢事; 编译器只需要捕获规范说它需要捕获的那些.错误检测*昂贵*且*困难*,因此与任何功能一样,错误检测算法必须在成本和收益方面优先于其他功能.这种错误检测器的好处非常小,因为这是非常人为的代码,并且错误的后果是显而易见的. (7认同)
  • 如果我定义了`class Foo <T>:IFoo <T>,其中T:C,new(){}`会不实现接口?我知道,正如你所说,没有可以使用的实际类型`T`.是否有一些通用术语用于区分"实质性"实现(这里不存在)和"非实质性"实现(如`Foo <T>`)? (3认同)

Gra*_*esa 9

只要我们谈论琐事,我认为这是Eric Lippert尝试的有效实现:

class Program
{
    static void Main(string[] args)
    {
        D test = new D();
    }
}

class C
{
    private C() { }
}

interface IFoo<T> where T : C, new() { }

class D : C
{
    public D()
        : this(5) { }

    public D(int x)
        : this() { }
}

class Dfoo : IFoo<D> { }
Run Code Online (Sandbox Code Playgroud)

它可以很好地编译,但StackOverflowException在实例化时会崩溃D.

  • 十多年来我一直在C#编译器团队工作,而且我一直都在学习关于这种语言的新东西.我没有意识到编译器会允许你覆盖一个类,如果基类ctor从未被调用过,但当然这是完全合理的.谢谢你这个好榜样! (9认同)

xan*_*ded 7

如果您尝试分解旧接口,可以使用该ObsoleteAttribute属性标记接口.

编辑:正如@Magnus在评论中指出的那样,如果你将Error属性true设置它的使用将导致错误.

  • @Renan如果在ctor中设置了`error`参数,它几乎无法使用. (4认同)

sup*_*cat 5

如果类型可访问且未密封,则外部代码可以创建该类型的实例,并且基本类型无法对其进行任何操作.不需要"完全信任"或反思.

public class CantDeriveMe
{
    private CantDeriveMe()
    {
    }
    public override string ToString()
    {
        return "My type is " + this.GetType().ToString();
    }
}

public class OhYeah : CantDeriveMe
{
    static OhYeah CapturedInstance;

    ~OhYeah()
    {
        CapturedInstance = this;
    }

    OhYeah() : this(1/String.Empty.Length)
    {
    }
    OhYeah(int blah) : this()
    {
    }
    public static OhYeah Create()
    {
        try
        {
            new OhYeah(4);
        }
        catch (DivideByZeroException)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
        return CapturedInstance;
    }
    public static void test()
    {
        OhYeah it;
        it = OhYeah.Create();
        Console.WriteLine("Result was ({0})", it);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果代码仅在C#中编写,则基类析构函数可能会发出声音,如果它注意到该对象不是合法类型,但是用C#以外的语言编写的代码将允许覆盖Finalize退出而不链接到它家长.

我认为可以指定一个开放的通用接口,它具有组合struct和类约束,没有类型的组合可能实现,例如

public interface evil<T, U>
    where T : struct,U
    where U : class
Run Code Online (Sandbox Code Playgroud)

我不确定这样的开放泛型类型是否真的有资格作为"接口",或者只是封闭的泛型类型是否真的可以作为接口(或类或结构).

  • 圣洁的善良这是狡猾的.干得好.我想知道你是否可以根据这个进行攻击? (6认同)