簡體   English   中英

指定一個接口只能由引用類型 C# 實現

[英]Specify that an interface can only be implemented by reference types C#

如果我在 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成為引用類型( where T: IInterface, class在聲明中使用UsingType )。 有什么方法可以在接口級別執行此操作嗎?

public interface IInterface : class

不起作用(顯然),但也許還有另一種方法可以完成同樣的事情?

如果你在一個接口下傳遞一些東西,那么即使你有一個實現該接口的值類型,如果強制轉換為接口,它也會被裝箱並且表現得像一個引用類型(因為它被裝箱在一個引用類型中)。

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

因此,結構或 class 是否實現接口並不重要。 如果強制轉換為接口,然后在接口下傳遞,那么它將表現為引用類型。

編輯:所以如果這些是你的要求......

對於合同 X:

  1. 如果結構實現/繼承 X,則引發編譯錯誤。
  2. X 可能不是抽象的 class。

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

  • 如果結構實現/繼承合同,則獲得編譯錯誤的唯一方法是它是抽象 class。
  • 由於您不能使用抽象 class 來保持 inheritance 選項打開,因此您必須使用接口。
  • 強制執行結構無法實現接口的規則的唯一方法是在運行時。

使用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方法)並拋出ArgumentException來處理盒裝結構的情況。

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

不幸的是,我認為您不能以這種方式限制接口。 根據msdn接口可以由任何類型實現,包括結構和類:/

轉換為接口的可變結構的裝箱行為當然很煩人。 不過,我不知道是否有必要禁止所有結構。 在許多情況下,使用不可變結構包裝 class object 可能很有用(例如,實現支持多種枚舉方式的 Dictionary 之類的一種方法是使 Dictionary.Keys 和 Dictionary.Values 都為結構,每個都包含對 Dictionary 的引用,並且每個都提供特殊的 GetEnumerator 方法; Dictionary 沒有以這種方式實現,但它不是一個糟糕的模式)。 在直接調用 object 上的 GetEnumerator 的情況下,這種模式可以避免裝箱(因為 vb.net 和 c# 都使用它們的鴨式 foreach 循環); 因為結構是不可變的,即使發生裝箱也是性能問題而不是正確性問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM