Внедрение метода принудительного применения без интерфейсов или абстрактных классов в С#

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

API в некоторых случаях использует интерфейсы и абстрактные классы, чтобы заставить клиента использовать API для реализации конкретных методов.

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

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

Тип... не может иметь атрибут Name. Типы и типы значений без конструктора по умолчанию могут использоваться как элементы в ResourceDictionary.

Ошибка возникает из-за того, что необходимо создать его экземпляр: Inherited Window не может иметь имя?

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

Есть ли что-нибудь, что я могу сделать чисто, например, любое публичное исполнение или что-то еще?

Это структура:

API: IContext → ContextControl → (Абстрактные методы)

Модуль DLL ContextControl (API) → AppContextControl (методы переопределения)

Host Application pull AppContextControl

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

В модуле dev, если я вместо наследования от ContextControl реализую IContext, я теряю все установленные по умолчанию bodys, и модуль dev должен реализовывать намного больше.

Приветствия.

5 ответов

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

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


Два описанных вами способа - интерфейсы и абстрактные классы - являются (как я уверен, вы знаете) рекомендуемыми способами делать то, что вы описываете, по крайней мере, если вы хотите обеспечить реализацию реализации во время компиляции.

Единственный другой способ, о котором я знаю, - предоставить реализацию по умолчанию, которая бросает NotImplementedException(). Это даст вам ошибку во время выполнения, но, к сожалению, ничего не происходит во время компиляции.


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

Например, вы загружаете все классы, реализующие IMyPlugin, определенные как таковые

public interface IMyPlugin{
 void ***************();
 void YourMethod();
}

Пользователь реализует класс, который наследует его

public class UserClass:IMyPlugin{
....
}

Вы хотите заставить свой собственный код вместо YourMethod, а затем сгенерировать класс во время выполнения, который наследует от UserClass и в YourMethod, создавать свой собственный код и называть (или нет, в зависимости от вашей потребности) базу. Вызовите базу по всем другим методам, где пользователь должен обеспечить реализацию.

Это немного больше для вас, но он скрывает всю уродливость от конечного пользователя, просто заставляя его реализовывать интерфейс. Для менее уродливости сделайте этот интерфейс в 2 интерфейса

public interface IPlugin : IUserPlugin,ICreatorPlugin

И будьте понятны вашим пользователям, что, хотя классы должны внедрять IPlugin, все, что угодно, от ICreatorPlugin может быть оставлено пустым.

Еще лучшим решением было бы только разоблачить IUserPlugin и сделать еще больше работы на вашей стороне (ваш класс выполнения наследуется от пользовательского класса И ICreatorPlugin, а затем использовать утиную печать, чтобы попасть в IPlugin).


У меня недавно была аналогичная проблема (dnt знает, что это поможет в вашем случае). где я должен генерировать ошибки во время компиляции вместо Runtime с общей функциональностью. подход, который я использовал, был комбинацией Generics и Interfaces. Примеры...

1) Enum Ex: (перечисления строк С#) Задача заключалась в том, чтобы установить такие вещи, чтобы я не должен реализовывать каждый код во всем проекте и принудительно создавать конструктор.

public abstract class EnumEx<t> where T : EnumEx<t>
{
 private readonly string _displayValue;
 private readonly string _value;

 protected static ReadOnlyCollection<t> EnumExs;

 protected EnumEx(string displayValue, string value)
 {
 _displayValue = displayValue;
 _value = value;
 }

 public string DisplayValue
 {
 get { return _displayValue; }
 }

 public static T FromString(string option)
 {
 foreach (var enumEx in EnumExs.Where(enumEx => enumEx._value == option))
 {
 return enumEx;
 }
 Debug.WriteLine(string.Format("Exception in EnumEX FromString({0})", option));
 return null;
 }

 public override string ToString()
 {
 return _value ?? string.Empty;
 }
}
</t></t></t>

2) Deep Copy (общий метод создания глубокой копии всех элементов в коллекции) + редактируемая реализацияIEditableObject по настраиваемому списку

public abstract class CloneAbleBase<t> : ObservableObjectEx, ICloneable, IEditableObject
 where T : DataBase<t>, new()
{
 protected T CurrentData = new T();
 private T _backupData;
 private bool _isInEditMode;

 public object Clone()
 {
 var dataObject = (CloneAbleBase<t>) MemberwiseClone();
 dataObject.CurrentData = CurrentData.Copy();
 return dataObject;
 }

 public void BeginEdit()
 {
 if (_isInEditMode) return;
 _backupData = CurrentData.Copy();
 _isInEditMode = true;
 }

 public void EndEdit()
 {
 if (!_isInEditMode) return;
 _backupData = default(T);
 _isInEditMode = false;
 RaisePropertyChanged(string.Empty);
 }

 public void CancelEdit()
 {
 if (!_isInEditMode) return;
 CurrentData = _backupData;
 RaisePropertyChanged(string.Empty);
 }
}
</t></t></t>

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


Не можете ли вы использовать комбинацию наследования и интерфейсов? Другими словами, создайте virtual методы для методов, которые можно переопределить, и используйте интерфейсы для реализации методов, которые необходимо переопределить?

licensed under cc by-sa 3.0 with attribution.