문제

다음과 같은 상속 트리를 감안할 때 작동하는 방식으로 구현하는 가장 좋은 방법은 무엇입니까?

abstract class Foo<T> : IEnumerable<T>
{
    public abstract Bar CreateBar();
}

class Bar<T> : Foo<T>
{
    // Bar's provide a proxy interface to Foo's and limit access nicely.
    // The general public shouldn't be making these though, they have access
    // via CreateBar()
    protected Bar(Foo base)
    {
        // snip...
    }
}

class Baz<T> : Foo<T>
{
    public Bar CreateBar()
    {
        return new Bar(this);
    }
}

이것은 다음과 같이 실패합니다. 'Bar.Bar()' is inaccessible due to its protection level.

나는 생성자가 공개되는 것을 원하지 않으며, 상속하는 클래스 만 Foo 만들 수 있어야합니다 Bar에스. Bar 전문화 된 것입니다 Foo, 그리고 모든 유형의 Foo 하나를 만들 수 있어야합니다. 공공 내부는 사전 정의 된 확장의 대부분이 다음과 같이 여기에서 '옵션'입니다. Foo DLL 내부에있을 것이지만, 나중에 자신의 유형을 만들고 싶어하는 사람은 누구나 나중에 오는 사람이기 때문에이 대답이라고 생각합니다. Foo 또는 Baz (발생할 가능성이 높습니다) 기본값에 갇힐 것입니다. CreateBar() 그들의 요구를 충족시키지 못할 수도있는 구현.

아마도 그것을 멋지게 작동시키기 위해 이것을 리팩토링하는 방법이 있습니까? 나는 이것을 설계하려고 벽에 내 머리를 두드리고 있으므로 효과가 있습니다.

편집 (추가 정보) :

약간 더 구체적인 : FOO는 ienumerable과 Long Story가 짧은 것을 구현하고 있으며 Bar는 동일한 인터페이스를 제공하지만 해당 열거 가능한 객체의 제한된 하위 집합에 제공됩니다. 모든 Foo는 자신의 하위 세트 (예 : 바)를 만들어 반환 할 수 있어야합니다. 그러나 BAR은 프록시를 수행하고 범위 제한에 대해 걱정하기 때문에 FOO를 구현하고 싶은 모든 사람을 갖고 싶지 않습니다.

도움이 되었습니까?

해결책

좋아요, 새로운 대답 :

  1. 막대를 인터페이스와 콘크리트 클래스로 분할하십시오.
  2. IBAR의 관점에서 공개 추상 방법을 표현하십시오.
  3. Bar를 Foo에서 개인 중첩 클래스로 만들어 Ibar를 구현하십시오. Foo에서 호출 할 수있는 내부 생성자를 제공하십시오.
  4. Foo에 보호 된 방법을 작성하여 바 그 자체로부터 막대 인스턴스를 만듭니다. FOO에서 파생되는 클래스는이를 사용하여 프록시만으로 충분한 경우 추상 방법을 구현할 수 있으며 더 복잡한 요구가있는 클래스는 IBAR을 직접 구현할 수 있습니다. 추상 방법을 가상 방법으로 변경하고 기본적으로 "this"에서 새 막대를 만들 수도 있습니다.

편집 : 이것의 한 가지 변형은 막대를 만드는 것입니다. 보호 공개 생성자와 함께 Foo 내 중첩 클래스. 그렇게하면 어떤 파생 클래스는 스스로 인스턴스화 할 수 있지만 관련이없는 클래스는 전혀 "볼"수 없습니다. 인터페이스를 구현과 분리해야합니다 (인터페이스가 공개 될 수 있음). 어쨌든 좋은 일이라고 생각합니다.

다른 팁

BAZ를 막대 내에서 중첩 된 유형으로 만들 수 있습니까? 그것이 바로 그 것보다 막대에 더 많은 액세스를 줄 수있는 유일한 방법입니다. 동일한 부모 클래스 만 있으면 Foo의 보호 된 구성원에게만 액세스 할 수 있으며 Foo는 바에 특별한 액세스 권한이 없습니다. 나는 중첩 된 유형 으로이 작업을 수행하는 다른 구불 구불 한 방법이 있다고 생각하지만 실제로 유지 보수 엔지니어에게는 매우 불쾌 할 것입니다.

그러나 하나의 파생 클래스가 동일한 기본 클래스에서 파생 된 다른 클래스의 인스턴스를 만들도록 강요하는 것은 매우 이상한 디자인입니다. 정말 당신이 필요한 것입니까? 아마도 이것을보다 구체적인 용어로 넣으면 대체 디자인을 만들기가 더 쉬울 것입니다.

foo 내 중첩 유형을 통해 바의 생성자에 액세스 할 수 있습니다.

abstract class Foo<T> : IEnumerable<T>
{
  public abstract Bar<T> CreateBar();

  protected Bar<T> CreateBar(Foo<T> f) { return new FooBar(f); }

  private class FooBar : Bar<T> 
   { public FooBar(Foo<T> f) : base(f) {}   
   }
}

class Bar<T> : Foo<T>
{ protected Bar(Foo<T> @base) {}
}

class Baz<T> : Foo<T>
{
    public override Bar<T> CreateBar() 
    {
        return CreateBar(this);
    }
}

잠시 동안 그 바는 Foo에서 파생됩니다. 이렇게하면 문제는 "서브 클래스가 막대 생성자에 액세스 할 수 없더라도 FOO의 모든 서브 클래스가 막대를 만들 수 있도록 어떻게해야합니까?"

해결하기 쉬운 문제입니다.

public class Foo
{
   protected static Bar CreateBarInstance()
   {
      return new Bar();
   }
   public virtual Bar CreateBar()
   {
      return CreateBarInstance();
   }
}

public class Bar
{
    internal Bar()
    {
    }
}

public class Baz : Foo
{
    public override Bar CreateBar()
    {
        Bar b = base.CreateBar();
        // manipulate the Bar in some fashion
        return b;
    }
}

당신이 원한다면 보장하다 FOO의 서브 클래스는 바의 생성자에 액세스 할 수 없으며 다른 어셈블리에 넣습니다.

이제 FOO에서 막대를 도출하려면 간단한 변화입니다.

public class Bar : Foo
{
    internal Bar()
    {
    }
    public override Bar CreateBar()
    {
        throw new InvalidOperationException("I'm sorry, Dave, I can't do that.");
    }

}

"나는 생성자가 공개되는 것을 원하지 않습니다." 확인하다.

"FOO에서 물려받은 클래스 만 막대를 만들 수 있어야합니다." 확인하다.

"모든 유형의 foo는 하나를 만들 수 있어야합니다." 확인하다,

"나중에 자신만의 foo 또는 baz를 만들고 싶어하는 사람은 (일어날 가능성이 높다는) 누구나 자신의 요구를 충족시키지 못할 수도있는 기본 CreateBar () 구현에 갇힐 것입니다." 이것은 foo.createbar () 메소드에서 일어날 일에 매우 크게 의존합니다.

C#은 C ++ Friend 키워드와 직접적인 동등한 것을 제공하지 않습니다. 당신의 디자인은 이런 종류의 구성을 요구하는 것 같습니다.

C ++에서는 특정 클래스가 "친구"를 사용하여 다른 클래스의 개인/보호 된 구성원에게 액세스 할 수 있음을 지정할 수 있습니다. 참고 : 이것은 동일한 어셈블리 내의 모든 클래스에 액세스 할 수있는 C# 내부 수정 자와 동일하지 않습니다.

디자인을 보면 C ++ 스타일의 친구가 필요한 라인을 따라 무언가를하려고하는 것 같습니다. Jon Skeet은 맞습니다. C#의 일반적인 디자인은 중첩 클래스를 사용하는 것입니다.

이것 포럼 게시물 더 설명 하고이 작업을 수행하는 방법에 대한 몇 가지 예를 보여줍니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top