#c_sharp
Существует следующая структура приложения: Реализация метода Id из интерфейса IGetId одинаковая везде. Но, исходя из этой структуры его нужно будет каждый раз переписывать в каждом классе, имплементирующего интерфейсы, порожденные от IGetId. Если бы не было BaseModel это в принципе не было бы проблемой, т.к. можно было бы вынести реализацию этого метода в какой нибудь абстрактный класс AGetId и от него наследоваться, но множественное наследование не поддерживается, а композиция в данном случае нарушает целостность. В PHP существует очень удобный инструмент - Trait. Он отлично решает данную проблему, но, к сожалению, в C# такого функционала нету. В C# есть нечто похожее - extension methods, хоть это и совершенно разные вещи. Я пробовал сделать что-то с использованием методов расширения, но получилось на мой взгляд "не очень": IGetId: public interface IGetId { int Id { get; } } Extension methods: public static class CoreExtensions { public static int Id(this IGetId obj) { return obj.Id; } } В любом случае, это не дало ожидаемого результата, мне по прежнем нужно реализовывать Id в каждом классе. Существуют ли какие-то средства для решения данной проблемы?
Ответы
Ответ 1
Я бы предложил такую иерархию наследования: // библиотечные интерфейсы interface IGetId { int Id { get; } } interface IPerson : IGetId { string Name { get; } } interface IAddres : IGetId { string City { get; } } // универсальный базовый класс class BaseModel { /* ... */ } // вспомогательный класс abstract class BaseModelWithId : BaseModel, IGetId { int id; public int Id => id; } // реальные классы class Person : BaseModelWithId, IPerson { public string Name { get; set; } } Необходимость реализовывать IGetId в Person отпала. Хотя в общем случае вы правы, trait'ов не хватает. Есть хорошие шансы, что они появятся в будущем C# 8.Ответ 2
Замените наследование композицией: class Id { public int Value { get; set; } public Id(int value) => Value = value; public override bool Equals(object obj) => ((obj is Id id) && Value.Equals(id.Value)) || ((obj is int i) && Value.Equals(i)); public override int GetHashCode() => Value.GetHashCode(); public static implicit operator int(Id id) => id.Value; public static implicit operator Id(int i) => new Id(i); } Ну, действительно, классы же ваши включают в себя Id. Используем: interface IPerson { string Name { get; } } class Person : IPerson { public Id Id { get; set; } public string Name { get; set; } } Вместо интерфейса IGetId теперь вы везде используете класс Id и его свойство Value.Ответ 3
Для соблюдения плюрализма мнений, добавлю вариант с идентификатором в базовом классе: interface IGetId { int Id { get; } } interface IPerson : IGetId { string Name { get; } } interface IAddres : IGetId { string City { get; } } Базовый класс: class BaseModel : IGetId { public virtual int Id { get { /* ... */ } } } Конкретный class PersonModel : BaseModel, IPerson { public string Name { get; set; } } (Я бы переименовал интерфейс IGetId во что-то на подобии IМодельСИдентификатором и, как следствие базовую модель)Ответ 4
С помощью методов расширения действительно можно решить вашу проблему. Делается это следующим образом. Из интерфейса убираем свойство Id. Остаётся просто: public interface IGetId { } В методе расширения пишем код получения Id: public static class CoreExtensions { public static int Id(this IGetId obj) { return 42; } } Можно использовать: var person = new Person(); Console.WriteLine(person.Id()); // вызывается extension method IGetId g = person; Console.WriteLine(g.Id()); // аналогично
Комментариев нет:
Отправить комментарий