[c#] 새 키워드가 필요한 이유는 무엇이며 기본 동작을 숨기고 재정의하지 않는 이유는 무엇입니까?

블로그 게시물 을보고 다음과 같은 질문이있었습니다.

  • new키워드 가 필요한 이유는 기본 클래스 메서드가 숨겨 지도록 지정하는 것입니다. 내 말은, 우리는 왜 그것을 필요로합니까? override키워드를 사용하지 않으면 기본 클래스 메서드를 숨기지 않습니까?
  • C #의 기본값이 숨기고 재정의하지 않는 이유는 무엇입니까? 디자이너가이 방식으로 구현 한 이유는 무엇입니까?



답변

좋은 질문입니다. 다시 말하겠습니다.

다른 방법으로 방법을 숨기는 것이 합법적 인 이유는 무엇입니까?

예를 들어 그 질문에 답하겠습니다. CLR v1의 인터페이스가 있습니다.

interface IEnumerable
{
    IEnumerator GetEnumerator();
}

감독자. 이제 CLR에서 당신은 제네릭이 있고 V1 만 우리가 가진 거라고 제네릭 내가이 일반적인 인터페이스를 만들었을 것입니다 경우 “사람이.하지만. 내가 지금과 호환 뭔가해야하지 않았다고 생각 V2 이다 일반적인 그래서를 IEnumerable을 기대하는 코드와의 하위 호환성을 잃지 않고 제네릭의 이점을 얻었습니다. “

interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> .... uh oh

무엇의 GetEnumerator 메서드를 호출 할 예정 IEnumerable<T>입니까? 당신이 기억 하고자 는 제네릭이 아닌 기본 인터페이스에서 GetEnumerator를 숨길. 당신은 결코 당신이 이전 버전과의 compat 상황에서 명시 적 아니라면 그 일이 호출되고 싶지 않습니다.

그것만으로도 방법 숨기기가 정당화됩니다. 메서드 숨김의 정당화에 대한 자세한 내용 은 주제에 대한 내 기사를 참조하십시오 .

“new”없이 숨기면 경고가 발생하는 이유는 무엇입니까?

우리는 당신이 무언가를 숨기고 있고 우연히 그것을 할 수 있다는 것을 당신에게 알리고 싶기 때문입니다. 파생 클래스를 편집하는 대신 다른 사람이 기본 클래스를 편집하여 실수로 무언가를 숨기고있을 수 있습니다.

왜 “새로운”없이 숨는 것은 오류가 아니라 경고입니까?

같은 이유. 새 버전의 기본 클래스를 선택했기 때문에 실수로 무언가를 숨기고있을 수 있습니다. 이것은 항상 발생합니다. FooCorp는 기본 클래스 B를 만듭니다. BarCorp는 고객이 해당 메서드를 좋아하기 때문에 Bar 메서드를 사용하여 파생 클래스 D를 만듭니다. FooCorp는 그것을보고 좋은 생각이라고 말합니다. 우리는 그 기능을 기본 클래스에 넣을 수 있습니다. 그들은 그렇게하고 Foo.DLL의 새 버전을 제공하고 BarCorp가 새 버전을 선택할 때 자신의 메서드가 이제 기본 클래스 메서드를 숨긴다는 말을 들으면 좋을 것입니다.

우리는 그 상황이 오류가 아닌 경고가 되기를 원합니다. 왜냐하면이를 오류로 만드는 것은 이것이 취약한 기본 클래스 문제의 또 다른 형태 임을 의미하기 때문 입니다. C #은 누군가 기본 클래스를 변경할 때 파생 클래스를 사용하는 코드에 미치는 영향을 최소화하도록 신중하게 설계되었습니다.

숨기고 기본값을 재정의하지 않는 이유는 무엇입니까?

가상 재정의는 위험하기 때문 입니다. 가상 재정의를 사용하면 파생 클래스가 기본 클래스를 사용하도록 컴파일 된 코드의 동작을 변경할 수 있습니다. 당신이 뭔가해야 재정의를 만드는 같은 뭔가 위험한 일을 의식적으로 그리고 의도적으로 하지 사고에 의해를.


답변

파생 클래스의 메서드 앞에 new 키워드가있는 경우 메서드는 기본 클래스의 메서드와 독립적 인 것으로 정의됩니다.

그러나 new 또는 override를 지정하지 않으면 결과 출력은 new를 지정한 것과 동일하지만 컴파일러 경고가 표시됩니다 (기본 클래스 메서드에서 메서드를 숨기고 있다는 것을 알지 못할 수 있으므로 또는 실제로 재정의하고 싶을 수 있으며 키워드를 포함하는 것을 잊었습니다.)

따라서 실수를 방지하고 원하는 작업을 명시 적으로 표시하고 코드를 더 읽기 쉽게 만들어 코드를 쉽게 이해할 수 있습니다.


답변

이 컨텍스트에서 의 유일한 효과 new는 경고를 억제하는 것입니다. 의미 체계에는 변화가 없습니다.

따라서 한 가지 대답은 다음과 같습니다. new숨김이 의도적 이라는 것을 컴파일러에 알리고 경고를 제거해야합니다.

후속 질문은 : 메서드를 재정의 할 수 없거나 재정의 할 수없는 경우 동일한 이름의 다른 메서드를 도입하는 이유는 무엇입니까? 은폐는 본질적으로 이름 충돌이기 때문입니다. 물론 대부분의 경우 피할 것입니다.

의도적으로 숨겨야한다고 생각할 수있는 유일한 이유는 인터페이스가 이름을 강요 할 때입니다.


답변

C #에서 멤버는 기본적으로 봉인되어 있습니다. 즉, virtual또는 abstract키워드 로 표시되지 않는 한이를 재정의 할 수 없으며 이는 성능상의 이유 때문 입니다. 새로운 수정은 명시 적으로 상속 된 멤버를 숨기는 데 사용됩니다.


답변

override키워드 를 지정하지 않고 재정의가 기본값 인 경우 이름이 같기 때문에 실수로 기본 메서드를 재정의 할 수 있습니다.

.Net 컴파일러 전략은 안전을 위해 무언가 잘못 될 수있는 경우 경고를 내보내는 것이므로이 경우 재정의가 기본값 인 경우 재정의 된 각 메서드에 대해 경고가 표시되어야합니다. 재정의 ‘.


답변

내 추측은 주로 다중 인터페이스 상속 때문입니다. 신중한 인터페이스를 사용하면 두 개의 서로 다른 인터페이스가 동일한 메서드 서명을 사용할 수 있습니다. new키워드 의 사용을 허용하면 두 개의 별개의 클래스를 만드는 대신 하나의 클래스로 이러한 다른 구현을 만들 수 있습니다.

업데이트Eric이이 예제를 개선하는 방법에 대한 아이디어를 제공했습니다.

public interface IAction1
{
    int DoWork();
}
public interface IAction2
{
    string DoWork();
}

public class MyBase : IAction1
{
    public int DoWork() { return 0; }
}
public class MyClass : MyBase, IAction2
{
    public new string DoWork() { return "Hi"; }
}

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        var ret0 = myClass.DoWork(); //Hi
        var ret1 = ((IAction1)myClass).DoWork(); //0
        var ret2 = ((IAction2)myClass).DoWork(); //Hi
        var ret3 = ((MyBase)myClass).DoWork(); //0
        var ret4 = ((MyClass)myClass).DoWork(); //Hi
    }
}


답변

언급했듯이, 메소드 / 속성 숨김은 다른 방법으로는 쉽게 변경할 수없는 메소드 또는 속성에 대한 변경을 가능하게합니다. 이것이 유용 할 수있는 한 가지 상황은 상속 된 클래스가 기본 클래스에서 읽기 전용 인 읽기-쓰기 속성을 갖도록 허용하는 것입니다. 예를 들어 기본 클래스에 Value1-Value40이라는 읽기 전용 속성이 있다고 가정합니다 (물론 실제 클래스는 더 나은 이름을 사용합니다). 이 클래스의 봉인 된 하위 항목에는 기본 클래스의 개체를 가져와 여기에서 값을 복사하는 생성자가 있습니다. 그 후에는 클래스가 변경되는 것을 허용하지 않습니다. 상속 가능한 다른 하위 항목은 Value1-Value40이라는 읽기-쓰기 속성을 선언합니다.이 속성은 읽을 때 기본 클래스 버전과 동일하게 동작하지만 쓸 때는 값을 쓸 수 있습니다.

이 접근 방식 (아마 누군가 나를 도울 수 있음)의 한 가지 성가신 점은 동일한 클래스 내에서 특정 속성을 숨기고 재정의하는 방법을 모른다는 것입니다. CLR 언어가이를 허용합니까 (VB 2005 사용)? 기본 클래스 개체와 해당 속성이 추상적 인 경우 유용하지만 하위 클래스가 속성을 섀도 잉하기 전에 Value1에서 Value40 속성을 재정의하려면 중간 클래스가 필요합니다.