Укажите, что интерфейс может быть реализован только ссылочными типами C # - PullRequest
7 голосов
/ 05 июня 2011

Если я объявляю интерфейс в 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

не работает (очевидно), но, возможно, есть другой способ сделать то же самое?

Ответы [ 4 ]

3 голосов
/ 05 июня 2011

Если вы передаете что-то под интерфейсом, то даже если у вас есть тип значения, реализующий этот интерфейс, он будет упакован, если приведен к интерфейсу, и будет вести себя как ссылочный тип (потому что он заключен в рамку внутри ссылочного типа).

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 даже не будет работать постоянно. Если бы у меня был этот метод (основанный на тех же Foo и IFoo выше):

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 для обработки случая в штучной упаковке.

0 голосов
/ 07 июня 2011

Бокс поведения изменяемых структур, представленных как интерфейсы, безусловно, может раздражать. Я не знаю, что запрет на все структуры был бы необходим, хотя. В ряде случаев может быть полезно иметь неизменяемую структуру, обертывающую объект класса (например, один из способов реализации чего-то вроде словаря, который поддерживает несколько способов перечисления, состоял бы в том, чтобы Dictionary.Keys и Dictionary.Values ​​оба структуры, каждая из которых содержит ссылку на словарь, и каждая предоставляет специальный метод GetEnumerator; словарь не реализован таким образом, но это неплохой шаблон). Такой шаблон может избежать упаковки в случаях, когда кто-то вызывает GetEnumerator непосредственно для объекта (как vb.net и c # оба делают со своими типизированными утками циклами foreach); поскольку структуры являются неизменяемыми, даже когда происходит бокс, это скорее проблема с производительностью, чем с правильностью.

0 голосов
/ 05 июня 2011

Я не думаю, что вы можете ограничить интерфейсы таким образом, к сожалению.Согласно msdn интерфейсы могут быть реализованы любым типом, как структурами, так и классами: /

0 голосов
/ 05 июня 2011

http://msdn.microsoft.com/en-us/library/d5x73970.aspx. Похоже, вы можете указать, что это "класс", что означает ссылочный тип.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...