Извините, но я не смог найти ответ на этот вопрос в StackOverflow, хотя этот должен быть быть дубликатом. Я знаю, что множественное наследование невозможно.Многие пользовательские элементы управления, реализующие один и тот же интерфейс; есть ли способ избежать дублирования одного и того же кода в каждом?
Мое приложение имеет 50 классов пользовательских элементов управления, все из которых реализуют мой интерфейс IParamConfig.
IParamConfig определяет 5 свойств и 8 методов.
Из этих пользовательских элементов управления пользователя, некоторые наследуют от TextBox, некоторые из Баттона, некоторые из CheckBox, некоторые из ComboBox и т.д.
я мог бы создать класс ParamConfig, который реализует методы и свойства IParamConfig; но я знаю, что я не могу наследовать каждый из моих настраиваемых классов пользовательских элементов управления как из ParamConfig, так и из TextBox или другого пользовательского класса управления.
Поэтому я вынужден неоднократно дублировать реализацию методов и свойств IParamConfig.
Например, я создал абстрактный класс AbtrParamConfigTextBox, который наследует от TextBox и реализует методы и свойства IParamConfig; тогда я создал множество пользовательских элементов управления TextBox, которые наследуются от этого абстрактного класса.
Аналогичным образом, я создал абстрактный класс AbtrParamConfigComboBox, который наследуется от ComboBox, а также реализует методы и свойства IParamConfig.
В целом, я должен был дублировать общий код 12 раз (коробки в красный цвет на картинке).
Мне понравилось бы, если бы я мог использовать общий код только один раз.
Например, мне бы это понравилось, если бы у меня были все пользовательские элементы управления, наследуемые от одного и того же класса (ParamConfig).
Я попытался сделать это, создав класс ParamConfig, который наследуется от UserControl, а затем создать ParamConfigTextBox, который наследуется от ParamConfig, который включает в себя один TextBox.
Но это привело к неудобным обходным, потому что я больше не имею прямой доступ к свойствам в TextBox в ParamConfigTextBox, так что я должен дублировать их в ParamConfigTextBox, и добавить код, чтобы скопировать свойства из ParamConfigTextBox к его TextBox. Некрасиво.
Вопрос:
Am Я застрял в том, чтобы дублировать общий код в 12 раз?
Должен ли я использовать общий подход класса, наследующий от UserControl?
Или есть более чистое решение?
EDIT: Снимок экрана пользовательского интерфейса, как просили. У него есть ~ 100 вкладок, каждый из которых содержит пользовательские элементы управления для настройки продукта.
EDIT:
Интерфейс:
// Functions
string BmsParamName { get; } // Name of the parameter in the BMS's memory associated with this control
void InitControlOnLoad(); // Initialize the control after load
void ClearCtrl(); // Clear this control
uint BitsUsedMask(); // Return the mask for the bits used in memory
void ShowValue16(ushort paramWord, bool isBadData); // Update the value or state displayed by this control
void ShowValue32(uint paramValue32, bool isBadData); // Update the value or state displayed by this control
uint NoOfBmsMemWords(); // Return the number of BMS memory words that this set uses
void ShowParamArray(uint[] paramValues, uint bmsMemOfst, bool isBadData); // Update the value or state displayed by this control
ushort GetEntry16(ushort configWord); // Receive the full word of configuration data and return it after replacing those bits that this configuration control is responsible for
uint GetEntry32(); // Return the value in this control as a 16 bit unsigned integer
ushort[] GetEntryArray(); // Get the set of configuration words for this configuration control
Свойство:
#region Properties (Protected properties, available to inherited classes)
// Name of BMS variable
protected string bmsParamName = "";
[DefaultValueAttribute(0), Category("_Vinci"),
Description("Name of the variable in the BMS's memory associated with this control")]
public string BmsParamName
{
get { return bmsParamName; }
set { bmsParamName = value; }
}
// Conversion factor
protected float conversionFactor = 1F;
[DefaultValueAttribute(1),
Description("To convert the BMS value to the value shown in this setting, divide by this factor"),
Category("_Vinci")]
public float ConversionFactor
{
get { return conversionFactor; }
set { conversionFactor = value; }
}
// Width of the data
protected VinciForm.DataWidth dataWidth = VinciForm.DataWidth.Data16;
[DefaultValueAttribute(VinciForm.DataWidth.Data16),
Description("Whether the data in the BMS fill an entire 32 bit word, a 16 bit Word, just the top 8 bits (MSB), the lower 8 bits (LSB), or specific bits"),
Category("_Vinci")]
public VinciForm.DataWidth DataWidth
{
get { return dataWidth; }
set
{
dataWidth = value;
firstBitNo = 0u;
switch (dataWidth)
{
case VinciForm.DataWidth.Data32:
noOfBits = 32u;
break;
case VinciForm.DataWidth.Data16:
noOfBits = 16u;
break;
case VinciForm.DataWidth.Data8LSB:
noOfBits = 8u;
break;
case VinciForm.DataWidth.Data8MSB:
firstBitNo = 8u;
noOfBits = 8u;
break;
case VinciForm.DataWidth.DataBits:
noOfBits = 16u;
break;
}
}
}
// Number of the least significant bit used
protected uint firstBitNo = 0u;
[DefaultValueAttribute(0), Category("_Vinci"),
Description("Number of the least significant bit used")]
public uint FirstBitNo
{
get { return firstBitNo; }
set { firstBitNo = value; }
}
// Number of bits used
protected uint noOfBits = 16u;
[DefaultValueAttribute(3), Category("_Vinci"),
Description("Number of bits used")]
public uint NoOfBits
{
get { return noOfBits; }
set { noOfBits = value; }
}
// Display format: unsigned, signed or hex
protected VinciForm.DisplayFormat displayFrmtCode = VinciForm.DisplayFormat.UnsignedFormat;
[DefaultValueAttribute(VinciForm.DataWidth.Data16),
Description("Display format: unsigned, signed or hex"),
Category("_Vinci")]
public VinciForm.DisplayFormat DisplayFormat
{
get { return displayFrmtCode; }
set { displayFrmtCode = value; }
}
// Format string
protected string displayFrmtStr = "";
[DefaultValueAttribute(""),
Description("Display format string (e.g.: F4 for 4 decimal places); leave blank for automatic"),
Category("_Vinci")]
public virtual string DisplayFrmtStr
{
get { return displayFrmtStr; }
set { displayFrmtStr = value; }
}
#endregion
Было бы хорошо видеть интерфейс. В противном случае ответы будут неопределенными. – Sefe
Почему у вас нет прямого доступа к TextBox? Вы сделали это частным? Если вы добавите его как свойство с защищенным модификатором доступа, дочерние классы должны иметь возможность использовать его напрямую. – chadnt
@Sefe Я добавил скриншот пользовательского интерфейса –