2017-01-28 4 views
2

미안하지만이 이어야 복제본인데 StackOverflow에서이 질문에 대한 답을 찾을 수 없었습니다. 나는 다중 상속이 불가능하다는 것을 알고있다.동일한 인터페이스를 구현하는 많은 사용자 지정 사용자 컨트롤. 각각에 동일한 코드가 중복되는 것을 피할 수있는 방법이 있습니까?

내 응용 프로그램에는 50 개 클래스의 사용자 지정 사용자 정의 컨트롤이 있으며, 모두 내 IParamConfig 인터페이스를 구현합니다.

IParamConfig는 5 개의 속성과 8 개의 메서드를 정의합니다. 이러한 사용자 정의 사용자 컨트롤의

가, 일부는 텍스트 상자에서 상속 등의 버튼에서 몇 가지 체크 상자의 일부, 콤보 상자에서 일부

나는 방법 및 IParamConfig의 속성을 구현하는 ParamConfig 클래스를 만들 수 있습니다; 하지만 나는 각 사용자 정의 컨트롤 클래스가 ParamConfig와 TextBox 또는 다른 사용자 컨트롤 클래스로부터 상속받을 수 없다는 것을 알고 있습니다.

따라서 IParamConfig의 메서드 및 속성 구현을 여러 번 반복해야합니다.

예를 들어 TextBox에서 상속받은 추상 클래스 AbtrParamConfigTextBox를 만들고 IParamConfig의 메서드 및 속성을 구현합니다. 그런 다음이 추상 클래스에서 상속 된 많은 TextBox와 같은 사용자 지정 사용자 정의 컨트롤을 만들었습니다.

마찬가지로, ComboBox에서 상속 받고 IParamConfig의 메서드 및 속성도 구현하는 추상 클래스 AbtrParamConfigComboBox를 만들었습니다.

Custom user controls inherit from various standard user controls

총에, 나는 (그림에서 빨간색 상자) 공통의 코드 12 번을 복제했다.

공통 코드를 한 번만 표시 할 수 있다면 좋겠어요.

예를 들어 모든 사용자 지정 사용자 지정 컨트롤이 동일한 클래스 (ParamConfig)에서 상속받을 수 있다면 좋겠습니다.

enter image description here

All custom user controls inherit from a single class

나는 일을 시도하는 UserControl에서 상속 ParamConfig 클래스를 생성 한 다음 하나의 텍스트 상자를 포함 ParamConfig에서 상속 ParamConfigTextBox을 만들어.

Single Parent class

그러나 ParamConfigTextBox의 속성을 복사하는 코드를 내가 더 이상 ParamConfigTextBox에서 TextBox의 속성에 직접 액세스 할 수 없습니다, 그래서 ParamConfigTextBox에서 그들을 복제해야하고, 추가하기 때문에 즉, 어색 해결 방법 결과 텍스트 상자에. 추한.

질문 : 공통 코드 12 번을 복제하는 데에 갇혀

건가요?

UserControl에서 상속하는 공통 클래스 방식을 추구해야합니까?

더 깨끗한 해결책이 있습니까?

EDIT : 요청한대로 사용자 인터페이스의 스크린 샷. enter image description here 이 제품에는 ~ 100 개의 탭이 있으며, 각 탭에는 제품 구성을위한 사용자 컨트롤이 가득합니다.

편집 :

인터페이스 :

// 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 
+1

인터페이스를 확인하는 것이 좋습니다. 그렇지 않으면 응답이 모호합니다. – Sefe

+0

왜 TextBox에 직접 액세스 할 수 없습니까? 비공개로 만들었 니? 보호 된 액세스 수정자가있는 특성으로이를 추가하는 경우, 하위 클래스는이를 직접 사용할 수 있어야합니다. – chadnt

+0

@Sefe 사용자 인터페이스의 스크린 샷을 추가했습니다 –

답변

1

사용자 인터페이스는 예에서 구현하는 컨트롤의 속성 (적어도 속성에 액세스하지 않는 것). 즉, 자신의 클래스에 캡슐화하고 다시 사용하는 것이 매우 쉽다는 것을 의미합니다. 컨트롤의 일부 속성에 액세스하는 경우에도 별도의 클래스에 인터페이스를 구현하고 콜백 메서드와 같은 기술을 사용하여 컨트롤 특정 부분에 액세스 할 수 있습니다 (예를 들어 줄 수는 없습니다. 게시물의 인터페이스).

그런 다음 컨트롤에서이 클래스의 인스턴스를 사용할 수 있으므로 구현을 제어하고 코드를 다시 저장하십시오. 이것이 실제로 일반적인 디자인 패턴 (bridge, visitor 또는 adaptor)의 많은 부분입니다. 상속을 composition으로 대체하십시오.

interface IParamConfig 
{ 
    string BmsParamName 
    { 
     get; 
     set; 
    } 

    float ConversionFactor 
    { 
     get; 
     set; 
    } 
} 

당신은 별도의 헬퍼 클래스에서이 구현하는 것이 :

public class ParamConfig : IParamConfig 
{ 
    private string bmsParamName = ""; 
    [DefaultValueAttribute(0), 
    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 
    private float conversionFactor = 1F; 
    [DefaultValueAttribute(1), 
    Description("To convert the BMS value to the value shown in this setting, divide by this factor")] 
    public float ConversionFactor 
    { 
     get { return conversionFactor; } 
     set { conversionFactor = value; } 
    } 
} 

내가의 속성의 일부를 왼쪽

나의 점을 설명하기 위해, 나는 당신의 인터페이스의 단순화 된 버전을 사용 속성 편집기인데, 헬퍼 클래스 일지라도. 내가 왜 그랬는지 나중에 알게 될거야. 당신이 당신의 현재 구현에 가까운 곳에 머무르고 싶은 경우에, 당신은 당신의 컨트롤에 IParamConfig를 구현하고 헬퍼 클래스의 기능 전달할 수 있습니다 :

public class ParamConfigTextBox : TextBox, IParamConfig 
{ 
    private readonly ParamConfig paramConfig = new ParamConfig(); 

    [DefaultValueAttribute(0), Category("_Vinci"), 
    Description("Name of the variable in the BMS's memory associated with this control")] 
    public string BmsParamName 
    { 
     get { return paramConfig.BmsParamName; } 
     set { paramConfig.BmsParamName = value; } 
    } 

    // Conversion factor 
    [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 paramConfig.ConversionFactor; } 
     set { paramConfig.ConversionFactor = value; } 
    } 
} 

인정 하듯이,이 두 가지 속성은로 오프로드 너무 단순 조금 것 같다 도우미 클래스이지만, 거기에 모든 것을 넣을 것입니다. 더 복잡한 것도 있습니다. 전반적으로 최적의 코드 재사용을 얻을 것입니다.

이 솔루션의 단점은 모든 컨트롤에 전체 인터페이스를 구현해야한다는 것입니다. 기능이 도우미 클래스에 있지만 여전히 구현의 발목을 꽤 많이 잡아야합니다.

interface IParamConfigProvider 
{ 
    IParamConfig ParamConfig 
    { 
     get; 
    } 
} 

public class ParamConfigTextBox : TextBox, IParamConfigProvider 
{ 
    private readonly paramConfig = new ParamConfig(); 

    [Category("_Vinci"), 
    Description("The param config properties")] 
    public ParamConfig ParamConfig 
    { 
     get { return paramConfig; } 
    } 

    IParamConfig IParamConfigProvider.ParamConfig 
    { 
     get { return ParamConfig; } 
    } 
} 

당신은 여전히 ​​비주얼 디자이너에 IParamConfig의 proprties을 탐색 할 수 있습니다 : 당신은 (는 전체 인터페이스, 아니 간단한 예입니다이 시간)를 구현하는 것이 훨씬 쉽다 공급자 인터페이스를 생성하여 작업을 단순화 할 수 있습니다 (디자이너 속성을 유지했다는 것을 기억하십시오 - 이제 왜 그런지 알 것입니다 : 당신은 여전히 ​​에디터에서 속성을 볼 것입니다). 이것은 컨트롤 레벨에서 이것을 구현하는 가장 쉬운 방법이지만 IParamConfig에 액세스하는 코드는 IParamConfigProvider을 통과하도록 변경해야합니다. 결국 당신을 위해 봉사하는 것이 당신의 선택입니다.

+0

와우! 좋아, 나는 그것을 거의 얻는다. 완전히 소화하기까지는 시간이 걸릴 것입니다. 네가 가고 있다는 것을 나는 알기 때문에 나는 그것을 좋아한다. 내가 그걸 가지고 노는 후에 내가 너에게 돌아갈거야, 고마워! –

+1

XAML을 사용하지 않아서 첨부 된 속성을 사용할 수도 있습니다. –

+0

@Michael Puckett II : 저를 알게 해주셔서 감사합니다. –