8

如果我在 C# 中声明一个接口,有什么方法可以显式声明实现该接口的任何类型都是引用类型?

我想这样做的原因是,无论我将接口用作类型参数,我都不必指定实现类型也必须是引用类型。

我想要完成的示例:

public interface IInterface
{
    void A();
    int B { get; }
}

public class UsingType<T> where T : IInterface
{
    public void DoSomething(T input)
    {
         SomeClass.AnotherRoutine(input);
    }
}

public class SomeClass
{
    public static void AnotherRoutine<T>(T input)
        where T : class
    {
        // Do whatever...
    }
}

由于 to 的参数必须SomeClass.AnotherRoutine()是引用类型,所以在这里我会在调用该方法时遇到编译器错误,这表明我强制T成为引用类型(where T : IInterface, class在 的声明中UsingType)。有什么方法可以在接口级别执行此操作吗?

public interface IInterface : class

不起作用(显然),但也许还有另一种方法可以完成同样的事情?

4

4 回答 4

4

如果你在一个接口下传递一些东西,那么即使你有一个实现该接口的值类型,如果强制转换为接口,它也会被装箱并且表现得像一个引用类型(因为它被装箱在一个引用类型中)。

interface IFoo {
    int Value { get; set; }
}

struct Foo : IFoo {
    public int Value { get; set; }
}

观察用作值类型时的效果:

var a = new Foo() { Value = 3 };
var b = a; // copies value
b.Value = 4;
Console.WriteLine( "a has {0}", a.Value ); //output: a has 3
Console.WriteLine( "b has {0}", b.Value ); //output: b has 4

现在看看当你将它转换到接口时会发生什么:

var a = new Foo() { Value = 3 } as IFoo; //boxed
var b = a; // copies reference
b.Value = 4;
Console.WriteLine( "a has {0}", a.Value ); //output: a has 4
Console.WriteLine( "b has {0}", b.Value ); //output: b has 4

因此,结构或类是否实现接口并不重要。如果强制转换为接口,然后在接口下传递,那么它将表现为引用类型。

编辑:所以如果这些是你的要求......

对于合同 X:

  1. 如果结构实现/继承 X,则引发编译错误。
  2. X 可能不是抽象类。

好吧,你只是被卡住了,因为它们相互矛盾。

  • 如果结构实现/继承合同,则获得编译错误的唯一方法是它是一个抽象类。
  • 由于您不能使用抽象类来保持继承选项打开,因此您必须使用接口。
  • 强制执行结构无法实现接口的规则的唯一方法是在运行时。

使用约束where T: class, IFoo甚至不会一直有效。如果我有这种方法(基于相同FooIFoo以上):

static void DoSomething<T>(T foo) where T: class, IFoo {
    foo.Value += 1;
    Console.WriteLine( "foo has {0}", foo.Value );
}

那么在这种情况下它会抛出一个编译错误:

var a = new Foo(){ Value = 3 };
DoSomething(a);

但在这种情况下它会工作得很好:

var a = new Foo(){ Value = 3} as IFoo; //boxed
DoSomething(a);

因此,就我而言,使用where T: class, IFoo-style 约束,然后结构是否实现接口可能无关紧要,只要它被装箱即可。不过,取决于如果通过盒装结构检查 EF 会做什么。也许它会起作用。

如果它不起作用,至少通用约束可以让你部分到达那里,你可以检查foo.GetType().IsValueType(参考我DoSomething上面的方法)并抛出一个ArgumentException来处理盒装结构的情况。

于 2011-06-05T19:05:12.603 回答
0

http://msdn.microsoft.com/en-us/library/d5x73970.aspx。看起来你可以指定它是一个“类”,这意味着引用类型。

于 2011-06-05T18:48:04.427 回答
0

不幸的是,我认为您不能以这种方式限制接口。根据msdn接口可以由任何类型实现,包括结构和类:/

于 2011-06-05T18:53:42.863 回答
0

转换为接口的可变结构的装箱行为当然很烦人。不过,我不知道是否有必要禁止所有结构。在许多情况下,包装类对象的不可变结构可能很有用(例如,实现支持多种枚举方式的 Dictionary 之类的东西的一种方法是使 Dictionary.Keys 和 Dictionary.Values 都为结构,每个都包含对 Dictionary 的引用,并且每个都提供特殊的 GetEnumerator 方法; Dictionary 没有以这种方式实现,但它不是一个糟糕的模式)。在直接对对象调用 GetEnumerator 的情况下,这种模式可以避免装箱(因为 vb.net 和 c# 都使用它们的鸭子类型的 foreach 循环);因为结构是不可变的,即使确实发生了拳击”

于 2011-06-07T12:45:18.850 回答