指定接口只能通过引用类型C#实现

时间:2011-06-05 17:48:11

标签: c# generics interface reference-type

如果我在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...
    }
}

由于SomeClass.AnotherRoutine()的参数必须是引用类型,因此我将在此处调用该方法时出现编译器错误,建议我强制T为引用类型({{ 1 {}在where T : IInterface, class的声明中。有没有办法可以在界面级别强制执行此操作?

UsingType

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

4 个答案:

答案 0 :(得分:3)

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

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.   
  3. X可能不是抽象类。
  4.   

那么,你只是陷入困境,因为那些相互矛盾。

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

使用约束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 - 样式约束,然后只要它被装箱,结构实现接口可能无关紧要。但是,如果传递一个盒装结构,则取决于检查EF的作用。也许它会起作用。

如果它不起作用,至少通用约束会让你分开,你可以检查foo.GetType().IsValueType(参考我上面的DoSomething方法)并抛出{{1}处理盒装结构的情况。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

我认为你不能以这种方式限制接口。根据{{​​3}},接口可以通过任何类型实现,包括结构和类:/

答案 3 :(得分:0)

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