Считаете ли вы, что “автоматическая реализация интерфейса” была бы полезна в .NET / C # [закрыто]

StackOverflow https://stackoverflow.com/questions/656564

Вопрос

Подумайте об этом:

public class interface Person : IPerson
{
  int ID { get; protected set; }
  string FirstName { get; set; }
  string LastName { get; set; }
  string FullName { get { return FirstName + " " + LastName; } }
}

И это:

public class StubPerson : IPerson
{
    int ID { get { return 0; protected set { } }
    string FirstName { get { return "Test" } set { } }
    string LastName { get { return "User" } set { } }
    string FullName { get { return FirstName + " " + LastName; } }
}

Использование:

IPerson iperson = new Person();

Или:

IPerson ipersonStub = new StubPerson();

Или:

IPerson ipersonMock = mocks.CreateMock<IPerson>();

Таким образом, фактически мы объявляем ИПерсон интерфейс и Человек занятия в одно и то же время:

public class interface Person : IPerson

Как вы думаете, было бы полезно иметь такого рода поддержку в .NET / C #?

Редактировать:

Из-за массовой путаницы, я думаю, мне нужно уточнить предлагаемую цель:

Без этой функции вам пришлось бы писать:

interface IPerson
{
  int ID { get; }
  string FirstName { get; set; }
  string LastName { get; set; }
  string FullName { get; }
}

так же , как и это:

public class Person : IPerson
{
  int ID { get; protected set; }
  string FirstName { get; set; }
  string LastName { get; set; }
  string FullName { get { return FirstName + " " + LastName; } }
}

Я вообще не предлагаю никаких семантических изменений.

Это было полезно?

Решение

Я обдумал то же самое было некоторое время назад, особенно для использования в случае, когда у вас есть только одна производственная реализация интерфейса, но вы хотите смоделировать ее для тестирования.На данный момент это немного похоже на файлы .c / .h прошлых лет.

Я подозреваю, что в конце концов преимущества этого перевешиваются дополнительной сложностью как в языке, так и в последующем чтении кода.Тем не менее, мне все равно было бы интересно увидеть, как это будет изучено более тщательно.Даже тогда в моем списке приоритетов есть другие вещи намного выше - лучшая поддержка неизменяемости находится на самом верху :)

Другие советы

Позвольте мне посмотреть, понимаю ли я, о чем вы спрашиваете:

Почему мы не можем объявить интерфейс:

interface IPerson
{
    string Name {get;set;}
    int ID {get;set;}
}

И классы, которые реализуют этот интерфейс, будут наследовать его свойства без необходимости их повторного объявления:

class Person : IPerson { } 
//person now has properties Name and ID

Причина, по которой вы не можете этого сделать, заключается в том, что, хотя текст вашего кода интерфейса и вашего кода класса очень похожи, они имею в виду очень разные вещи.Интерфейс просто говорит: "разработчик будет иметь строковое имя с получателем и установщиком".Это класс, который говорит "возвращает закрытое поле при вызове средства получения имени". Даже если вы используете ярлык auto-property, чтобы позволить компилятору реализовать эту логику, это все равно Логические, который принадлежит к классу.Просто потому , что:

string Name {get;set;}

выглядит одно и то же в интерфейсе и в классе, это не означает даже отдаленно одно и то же.

Для компилятора было бы очень опасно реализовывать произвольную логику для выполнения ваших контрактов за вас, вместо того чтобы жаловаться во время компиляции, что вы их не реализовали.Это может привести к появлению ошибок, которые очень трудно отследить.Возвращать компиляторы к поведению по умолчанию, когда никакое поведение не определено, - это очень, очень плохая идея.

Я полагаю, что Eiffel делает что-то подобное в .NET, чтобы поддерживать множественное наследование.Объявление класса автоматически создает соответствующий интерфейс.Когда ссылаются на тип класса, компилятор в основном выдает ссылку на тип интерфейса вместо этого.Основное исключение, конечно, находится в выражениях конструктора.

Ну, я думаю, другие ответы помогут вам понять использование интерфейса для абстрактной логики в разных конкретных классах, я также думаю, что вы можете выполнить что-то похожее на то, что вы хотите, используя инструменты рефакторинга, встроенные в VS.

Определите свой класс...

public class Person
{
  public int ID { get; protected set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string FullName { get { return FirstName + " " + LastName; } }
}

Затем щелкните правой кнопкой мыши, выберите Рефакторинг -> Извлечь интерфейс.

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

Извлеченный интерфейс:

interface IPerson
{
    string FirstName { get; set; }
    string FullName { get; }
    int ID { get; }
    string LastName { get; set; }
}

Я предполагаю, что я упускаю суть - чего вы добиваетесь, смешивая класс и интерфейс вместе?Какую проблему вы решаете с помощью такого подхода?

Это:

IPerson iperson = new Person();

это уже легально в C #.

Редактировать: Для пояснения - приведенный выше код является законным, учитывая следующее:

interface IPerson { }

class Person : IPerson { }

Я бы, по крайней мере, хотел, чтобы Visual Studio реализовала мои свойства из интерфейса как автоматические свойства, если я попрошу ее сделать это.

К сожалению, эта опция отсутствует, и мне приходится иметь дело с не реализованными заглушками исключений

Нет, потому что вы были бы вынуждены предоставить доступ ко всем открытым элементам интерфейса.Попробуй Перетачиватель, и никогда больше не беспокойтесь об этом.

Resharper может предоставить эту функциональность, например,

  1. Сначала вы можете написать свой персональный класс.
  2. Вы можете извлечь свой интерфейс с помощью подтягивание элементов вверх к интерфейсу IPerson.

Следовательно, вы можете попросить Visual Studio автоматически сгенерировать заглушки реализации для вас.

Обновить

В любом случае, давайте сначала расскажем об интерфейсах, сославшись на код, который вы указали в своем вопросе:

public class interface Person : IPerson
{
    int ID { get; protected set; }
    string FirstName { get; set; }
    string LastName { get; set; }
    string FullName { get { return FirstName + " " + LastName; } }
}

Вы должны понимать, что Интерфейс - это не Абстрактный класс.Интерфейс - это просто контракт, своего рода схема, что означает, что она будет сообщать объекту, чего ожидать от другого объекта, на самом деле не заботясь о том, как это реализовано.

Абстрактный класс, с другой стороны, может содержать фрагменты функциональности, которые могут быть унаследованы и переопределены.

В приведенном выше случае ваш "интерфейс" недействителен, потому что:

  • вы не могли бы объявить ограничения области видимости для интерфейсов (общедоступных, частных, защищенных, внутренних), поскольку это деталь реализации
  • вы не смогли объявить реализацию по умолчанию (например, ваш FullName свойство) потому что, опять же, это деталь реализации

Мне кажется, что вам действительно нужен абстрактный класс, например,

public abstract class BasePerson
{
    public abstract int ID { get; protected set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public virtual string FullName { get { return FirstName + " " + LastName; } } 
}

Я просто предполагаю, но, возможно, это то, что вам действительно нужно.

ОБНОВЛЕНИЕ 2

Хорошо, я думаю, я добрался до того, чего вы хотите добиться, так что все, чего вы хотите, - это иметь возможность написать это:

public interface IPerson
{
    int ID { get; set; }
    string FirstName { get; set; }
    string LastName { get; set; }
    string FullName { get; }
}

И тогда для вашей реализации нужно будет только написать это:

public class Person : IPerson
{
    public int ID { get; protected set; }
    public string FullName { get { return FirstName + " " + LastName; } } 
}

Без необходимости указывать свойства FirstName и LastName.

Первая проблема, которую нам нужно решить, заключается в том факте, что интерфейсы не допускают разделителей доступа в своей реализации:что бы произошло, так это то, что свойства унаследовали бы разделитель доступа по умолчанию, который является закрытым.

Во-вторых, это тот факт, что, хотя в наших глазах string FirstName { get; set; } в интерфейсе и public string FirstName { get; set; } в классе одинаковы, они на самом деле не:

  • в интерфейсе определение свойства будет указывать, что сигнатуры методов для методов получения и / или установки будут доступны для всех классов, реализующих этот интерфейс.
  • в классе определение свойства будет указывать CLR создать анонимный объект, который будет содержать значение указанного свойства.

Тонкая разница для программиста, совершенно разные миры для компилятора.

Наконец, когда вы указываете, что вы реализуете интерфейс, Visual Studio выполняет synctactic magic, которая автоматически создает для вас заглушки этих свойств.

Я думаю, что лучшей абстракцией для этого является черта, или, как я описал здесь, роль.Это похоже на интерфейс с кодом.Ваш пример может быть закодирован следующим образом:

public role RPerson { 
  int ID { get; protected set; } 
  string FirstName { get; set; } 
  string LastName { get; set; } 
  string FullName { get { return FirstName + " " + LastName; } } 
} 

public class Person : RPerson { }

public class StubPerson : RPerson { 
    int ID { get { return 0; protected set { } } 
    string FirstName { get { return "Test" } set { } } 
    string LastName { get { return "User" } set { } } 
    string FullName { get { return FirstName + " " + LastName; } } 
} 

// ...

RPerson rperson = new Person(); 

RPerson rpersonStub = new StubPerson(); 
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top