서브 클래스에서 필드 또는 속성 재정의


145

추상 기본 클래스가 있으며이 부모 클래스에서 상속되는 각 클래스마다 다른 값을 갖는 필드 또는 속성을 선언하고 싶습니다.

기본 클래스 메소드에서이를 참조 할 수 있도록 기본 클래스 메소드에서이를 정의하려고합니다. 예를 들어 "이 오브젝트는 특성 / 필드 유형"이라는 ToString을 대체합니다 . 나는 이것을 할 수있는 세 가지 방법을 가지고 있지만 궁금합니다.이 일을하는 가장 좋은 방법은 무엇입니까? 초보자 질문입니다. 죄송합니다.

옵션 1 :
추상 속성을 사용하여 상속 된 클래스에서 재정의하십시오. 이 기능을 적용하면 (재정의해야 함) 이점이 있으며 깨끗합니다. 그러나 필드를 캡슐화하는 대신 하드 코드 값을 반환하는 것은 약간 잘못 느끼고 단지 코드가 아닌 몇 줄의 코드입니다. 또한 "세트"에 대해 본문을 선언해야하지만 덜 중요합니다 (아마도 내가 모르는 것을 피할 수있는 방법이 있습니다).

abstract class Father
{
    abstract public int MyInt { get; set;}
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
        set { }
    }
}

옵션 2
공용 필드 (또는 보호 된 필드)를 선언하고 상속 된 클래스에서 명시 적으로 재정의 할 수 있습니다. 아래 예제는 "new"를 사용하라는 경고를 주며 아마 그렇게 할 수 있지만 잘못 느꼈고 다형성을 깨뜨 렸습니다. 좋은 생각이 아닌 것 같습니다 ...

abstract class Mother
{
    public int MyInt = 0;
}

class Daughter : Mother
{
    public int MyInt = 1;
}

옵션 3
보호 필드를 사용하고 생성자에서 값을 설정할 수 있습니다. 이것은 꽤 깔끔해 보이지만 생성자가 항상 이것을 설정하고 여러 개의 오버로드 된 생성자가 있으면 일부 코드 경로가 값을 설정하지 않을 가능성이 있습니다.

abstract class Aunt
{
    protected int MyInt;
}

class Niece : Aunt
{
    public Niece()
    {
        MyInt = 1;
    }
}

그것은 약간의 이론적 인 질문이며 대답은 유일한 안전한 옵션 이므로 옵션 1이어야한다고 생각 하지만 C #에 익숙해지고 더 많은 경험을 가진 사람들에게 물어보고 싶었습니다.


abstract public int MyInt {get; set;} => 공개 추상 문자열 IntentName {get; set;} : D
Navid Golforoushan 2016 년

답변:


136

세 가지 솔루션의 옵션 1다형성 .

필드 자체는 무시할 수 없습니다. 이것이 바로 옵션 2새로운 키워드 경고를 반환하는 이유 입니다.

경고에 대한 해결책은“new”키워드를 추가하는 것이 아니라 옵션 1을 구현하는 것입니다.

필드가 다형성이어야하는 경우 속성으로 래핑해야합니다.

다형성 동작이 필요없는 경우 옵션 3 은 정상입니다. 그러나 런타임에 MyInt 특성에 액세스 할 때 파생 클래스는 리턴 된 값에 대한 제어가 없음을 기억해야합니다. 기본 클래스 자체는이 값을 반환 할 수 있습니다.

이것은 당신의 속성의 진정한 다형성 구현이 어떻게 보일지에 따라 파생 클래스가 제어되도록 합니다.

abstract class Parent
{
    abstract public int MyInt { get; }
}

class Father : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "X" and return a value */ }
    }
}

class Mother : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "Y" and return a value */ }
    }
}

153
따로, 나는 아버지 가 공식 "Y"를, 어머니는 논리적으로 "X"를 적용해야 한다고 생각합니다 .
피터-모니카 복원 복원

4
Parent에서 기본 구현을 제공하고 추상적이지 않은 경우 어떻게해야합니까?
Aaron Franke

@AaronFranke 서명을 만드십시오 : public virtual int MyInt {get; }
Ted Bigham

피터 ReinstateMonica @ 그것은 "유전 프로그래밍을"것
devinbost

18

옵션 2는 스타터가 아니며 필드를 무시할 수 없으며 숨길 수만 있습니다.

개인적으로 매번 옵션 1을 선택했습니다. 필드를 항상 비공개로 유지하려고합니다. 물론 실제로 속성을 무시할 수 있어야하는 경우입니다. 또 다른 옵션은 생성자 매개 변수에서 설정 한 기본 클래스에 읽기 전용 특성을 갖는 것입니다.

abstract class Mother
{
    private readonly int myInt;
    public int MyInt { get { return myInt; } }

    protected Mother(int myInt)
    {
        this.myInt = myInt;
    }
}

class Daughter : Mother
{
    public Daughter() : base(1)
    {
    }
}

인스턴스 수명 기간 동안 값이 변경되지 않는 경우 아마도 가장 적절한 방법 일 것입니다.


우리는이에 따라이 지금은 정확하지 말할 수 msdn.microsoft.com/en-us/library/9fkccyh4.aspx 당신이 속성을 재정의 할 수 있습니다은 MSDN 문서 쇼
codingbiz

1
@codingbiz : 내 답변은 속성에 대해 어디에서 이야기합니까? 필드와 속성은 동일하지 않습니다.
Jon Skeet

@codingbiz : (내 대답은 지금 은 물론 속성에 대해 이야기하지만, 결코 그것을 재정의 할 수 없다고 말한 적이 없습니다. 그것은 여전히 올바른 필드를 재정의 할 수 없다고 말했습니다 .)
Jon Skeet

7

옵션 2는 나쁜 생각입니다. 섀도 잉이라는 결과가 나옵니다. 기본적으로 두 개의 다른 "MyInt"멤버가 있습니다. 하나는 어머니와 다른 하나는 딸입니다. 이에 대한 문제는 어머니에서 구현 된 메소드가 어머니의 "MyInt"를 참조하고 딸에서 구현 된 메소드는 딸의 "MyInt"를 참조한다는 것입니다. 이로 인해 심각한 가독성 문제가 발생하고 나중에 혼란 스러울 수 있습니다.

개인적으로 최선의 선택은 3이라고 생각합니다. 명확한 중앙 집중식 값을 제공하고 자체 필드를 정의하는 번거 로움없이 내부적으로 참조 할 수 있기 때문에 옵션 1의 문제입니다.


6

당신은 이것을 할 수 있습니다

class x
{
    private int _myInt;
    public virtual int myInt { get { return _myInt; } set { _myInt = value; } }
}

class y : x
{
    private int _myYInt;
    public override int myInt { get { return _myYInt; } set { _myYInt = value; } }
}

virtual을 사용하면 무언가를하는 바디를 프로퍼티로 가져올 수 있으며 여전히 서브 클래스가이를 덮어 쓸 수 있습니다.


4

다음과 같이 정의 할 수 있습니다.

abstract class Father
{
    //Do you need it public?
    protected readonly int MyInt;
}

class Son : Father
{
    public Son()
    {
        MyInt = 1;
    }
}

값을 읽기 전용으로 설정하면 해당 클래스의 값이 객체 수명 동안 변경되지 않은 상태로 유지됩니다.

다음 질문은 왜 필요합니까?


정적은 단어의 선택이 좋지 않기 때문에 값이 클래스의 모든 인스턴스간에 공유됨을 의미하므로 물론 그렇지 않습니다.
윈스턴 스미스

3

클래스를 작성 중이고 특성의 기본 값이 필요한 virtual경우 기본 클래스에서 키워드 를 사용하십시오 . 이를 통해 선택적으로 속성을 재정의 할 수 있습니다.

위의 예를 사용하여 :

//you may want to also use interfaces.
interface IFather
{
    int MyInt { get; set; }
}


public class Father : IFather
{
    //defaulting the value of this property to 1
    private int myInt = 1;

    public virtual int MyInt
    {
        get { return myInt; }
        set { myInt = value; }
    }
}

public class Son : Father
{
    public override int MyInt
    {
        get {

            //demonstrating that you can access base.properties
            //this will return 1 from the base class
            int baseInt = base.MyInt;

            //add 1 and return new value
            return baseInt + 1;
        }
        set
        {
            //sets the value of the property
            base.MyInt = value;
        }
    }
}

프로그램에서 :

Son son = new Son();
//son.MyInt will equal 2

0

옵션 3을 사용 하겠지만 하위 클래스를 강제로 구현하는 추상 setMyInt 메소드가 있습니다. 이렇게하면 생성자에서 설정하는 것을 잊어 버린 파생 클래스의 문제가 발생하지 않습니다.

abstract class Base 
{
 protected int myInt;
 protected abstract void setMyInt();
}

class Derived : Base 
{
 override protected void setMyInt()
 {
   myInt = 3;
 }
}

그건 그렇고, 옵션 1을 사용하여 set을 지정하지 않으면; 추상 기본 클래스 속성에서 파생 클래스는이를 구현할 필요가 없습니다.

abstract class Father
{
    abstract public int MyInt { get; }
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
    }
}

0

생성자에서 속성 값을 요구하도록 추상 기본 클래스를 수정하면 옵션 3을 사용할 수 있습니다. 경로를 놓치지 마십시오. 나는이 옵션을 정말로 고려할 것입니다.

abstract class Aunt
{
    protected int MyInt;
    protected Aunt(int myInt)
    {
        MyInt = myInt;
    }

}

물론, 필드를 비공개로 설정 한 다음 필요에 따라 보호 또는 공공 재산 게터를 노출시킬 수도 있습니다.


0

나는 이걸했다...

namespace Core.Text.Menus
{
    public abstract class AbstractBaseClass
    {
        public string SELECT_MODEL;
        public string BROWSE_RECORDS;
        public string SETUP;
    }
}

namespace Core.Text.Menus
{
    public class English : AbstractBaseClass
    {
        public English()
        {
            base.SELECT_MODEL = "Select Model";
            base.BROWSE_RECORDS = "Browse Measurements";
            base.SETUP = "Setup Instrument";
        }
    }
}

이 방법으로 필드를 계속 사용할 수 있습니다.


프로토 타이핑이나 데모를위한 임시 솔루션으로 좋은 느낌입니다.
Zimano

0

구현을 가진 추상 클래스를 원할 때 구현 예제. 서브 클래스는 반드시 :

  1. 추상 클래스의 구현을 매개 변수화하십시오.
  2. 추상 클래스의 구현을 완전히 상속합니다.
  3. 직접 구현하십시오.

이 경우, 구현에 필요한 속성은 추상 클래스와 자체 하위 클래스를 제외하고는 사용할 수 없어야합니다.

    internal abstract class AbstractClass
    {
        //Properties for parameterization from concrete class
        protected abstract string Param1 { get; }
        protected abstract string Param2 { get; }

        //Internal fields need for manage state of object
        private string var1;
        private string var2;

        internal AbstractClass(string _var1, string _var2)
        {
            this.var1 = _var1;
            this.var2 = _var2;
        }

        internal void CalcResult()
        {
            //The result calculation uses Param1, Param2, var1, var2;
        }
    }

    internal class ConcreteClassFirst : AbstractClass
    {
        private string param1;
        private string param2;
        protected override string Param1 { get { return param1; } }
        protected override string Param2 { get { return param2; } }

        public ConcreteClassFirst(string _var1, string _var2) : base(_var1, _var2) { }

        internal void CalcParams()
        {
            //The calculation param1 and param2
        }
    }

    internal class ConcreteClassSecond : AbstractClass
    {
        private string param1;
        private string param2;

        protected override string Param1 { get { return param1; } }

        protected override string Param2 { get { return param2; } }

        public ConcreteClassSecond(string _var1, string _var2) : base(_var1, _var2) { }

        internal void CalcParams()
        {
            //The calculation param1 and param2
        }
    }

    static void Main(string[] args)
    {
        string var1_1 = "val1_1";
        string var1_2 = "val1_2";

        ConcreteClassFirst concreteClassFirst = new ConcreteClassFirst(var1_1, var1_2);
        concreteClassFirst.CalcParams();
        concreteClassFirst.CalcResult();

        string var2_1 = "val2_1";
        string var2_2 = "val2_2";

        ConcreteClassSecond concreteClassSecond = new ConcreteClassSecond(var2_1, var2_2);
        concreteClassSecond.CalcParams();
        concreteClassSecond.CalcResult();

        //Param1 and Param2 are not visible in main method
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.