2008-11-12 2 views
5

다음과 같은 이유, 아이디어가없는 이유는 무엇입니까? 공용 인터페이스는 IFieldSimpleItem {}"명시 적 인터페이스 구현을위한 제약 조건 ..."

public interface IFieldNormalItem : IFieldSimpleItem 
{ } 

public class Person 
{ 
    public virtual T Create<T>() 
     where T : IFieldSimpleItem 
    { 
     return default(T); 
    } 
} 

public class Bose : Person 
{ 
    public override T Create<T>() 
     where T : IFieldNormalItem //This is where the error is 
    { 
     return default(T); 
    } 
} 

내가이 일을하고 이유 때문에 개발자가 보스에서 상속하는 경우, 보스가 IFieldNormalItem의 적어도되고 생성되는 인스턴스에 의존한다는 사실이다. 아래는 IFieldSimpleItem에 의존하고 있지만 위의 내용은 최소한 IFieldNormalItem이되어야합니다.

public class Person 
{ 
    public virtual IFieldSimpleItem Create() 
    { 
     return null; 
    } 
} 

public class Bose : Person 
{ 
    public override IFieldSimpleItem Create() 
    { 
     return null; 
    } 
} 

건배 앤서니

답변

2

저는 컴파일러와 제네릭을 사용하여 런타임 확인을 저장하는 데있어 행운이라고 생각합니다. 이미 존재하지 않는 무언가를 재정의 할 수 없으며 동일한 메소드에 다른 반환 유형을 사용할 수 없습니다.

나는 당신의 동기를 완전히 이해한다고 말할 수는 없지만 기술적 인 장점이 있습니다.

첫 번째 시도는 가상이 아닌 공용 인터페이스가있는 기본 클래스를 사용하고 기본 클래스 Create가 호출되기 전에 체인의 모든 항목에서 유형을 검사 할 수있는 보호 된 가상 메서드 CheckCreatedType을 사용했습니다.

public class A 
{ 
    public IFieldSimpleItem Create() 
    { 
     IFieldSimpleItem created = InternalCreate(); 
     CheckCreatedType(created); 
     return created; 
    } 

    protected virtual IFieldSimpleItem InternalCreate() 
    { 
     return new SimpleImpl(); 
    } 
    protected virtual void CheckCreatedType(IFieldSimpleItem item) 
    { 
     // base class doesn't care. compiler guarantees IFieldSimpleItem 
    } 
} 
public class B : A 
{ 
    protected override IFieldSimpleItem InternalCreate() 
    { 
     // does not call base class. 
     return new NormalImpl(); 
    } 
    protected override void CheckCreatedType(IFieldSimpleItem item) 
    { 
     base.CheckCreatedType(item); 
     if (!(item is IFieldNormalItem)) 
      throw new Exception("I need a normal item."); 

    } 
} 

다음은 기본 클래스의 런타임 검사에서 달라 붙습니다.해결할 수없는 문제는 여전히 호출되는 기본 클래스 메서드에 의존해야한다는 것입니다. 오작동하는 하위 클래스는 base.CheckCreatedType(item)을 호출하지 않고 모든 검사를 중단 할 수 있습니다.

기본 클래스 안의 모든 하위 클래스에 대한 검사를 모두 하드 코딩하거나 (불량) 다른 방법으로 검사를 외부화합니다.

시도 2 ​​: (하위) 클래스는 필요한 수표를 등록합니다.

public class A 
{ 
    public IFieldSimpleItem Create() 
    { 
     IFieldSimpleItem created = InternalCreate(); 
     CheckCreatedType(created); 
     return created; 
    } 

    protected virtual IFieldSimpleItem InternalCreate() 
    { 
     return new SimpleImpl(); 
    } 

    private void CheckCreatedType(IFieldSimpleItem item) 
    { 
     Type inspect = this.GetType(); 
     bool keepgoing = true; 
     while (keepgoing) 
     { 
      string name = inspect.FullName; 
      if (CheckDelegateMethods.ContainsKey(name)) 
      { 
       var checkDelegate = CheckDelegateMethods[name]; 
       if (!checkDelegate(item)) 
        throw new Exception("failed check"); 
      } 
      if (inspect == typeof(A)) 
      { 
       keepgoing = false; 
      } 
      else 
      { 
       inspect = inspect.BaseType; 
      } 
     } 
    } 

    private static Dictionary<string,Func<IFieldSimpleItem,bool>> CheckDelegateMethods = new Dictionary<string,Func<IFieldSimpleItem,bool>>(); 
    protected static void RegisterCheckOnType(string name, Func<IFieldSimpleItem,bool> checkMethod) 
    { 
     CheckDelegateMethods.Add(name, checkMethod); 
    } 
} 
public class B : A 
{ 
    static B() 
    { 
     RegisterCheckOnType(typeof(B).FullName, o => o is IFieldNormalItem); 
    } 

    protected override IFieldSimpleItem InternalCreate() 
    { 
     // does not call base class. 
     return new NormalImpl(); 
    } 
} 

체크는 기본 클래스에서 호출 할 대리자를 등록하는 서브 클래스에 의해 수행하지만, 선행 모든 규칙을 알고있는 기본 클래스하지 않고있다. 비 - 가상 공용 인터페이스이기 때문에 기본 클래스가 결과를 반환하기 전에 검사 할 수 있습니다.

나는 당신이 잡으려고하는 개발자 오류라고 가정하고 있습니다. 해당되는 경우 런타임 검사 방법을 System.Diagnostics.Conditional("DEBUG")]으로 장식하여 릴리스 버전에서 검사를 건너 뛸 수 있습니다.

제네릭에 대한 지식이 완벽하지 않으므로 필요하지 않을 수 있습니다. 그러나 여기의 수표는 유형만으로는 안 될 필요가 없습니다. 다른 용도로 사용할 수 있습니다. 예 : 대리자가 Register..에 전달하면 참조가 특정 유형인지 확인할 필요가 없습니다. '

* 위와 같이 유형 이름으로 사전을 만드는 것은 좋지 않을 수 있습니다. 이 작업은 사용 된 메커니즘을 설명하기 위해 조금 단순합니다.

+0

감사합니다.이 검사가 실행 시간에 완료 되더라도 이것이 가장 가까운 것이라고 생각합니다. 건배 – vdhant

1

나는 문제가 이전에 정의 된 메소드를 오버라이드 (override)하는 것을 생각합니다. 그래서 효과적으로 당신은 허용되지 않는 메서드의 정의를 변경하려고합니다. 귀하의 유일한 선택은 새로운 방법을 생성하는 것입니다.

public class Bose : Person 
{ 
    public virtual T CreateNormal<T>() 
     where T : IFieldNormalItem //This is where the error is 
    { 
     return default(T); 
    } 
} 

또는 Person 클래스에 일반 필드가 필요하거나 동적으로 유효성 검사를 수행해야합니다. 이것에 대해

+0

나는 정의가 약하지 않고 강하게 만들기 때문에 괜찮을 것이라고 생각했을 것이다. – vdhant

0

무엇 :

public interface IFieldNormalItem : IFieldSimpleItem 
{ } 

public class Person<T> where T : IFieldSimpleItem 
{ 
    public virtual T Create() 
    { 
     return default(T); 
    } 
} 

지금 당신이 Person<IFieldSimpleItem>을 가질 수는 또는 Person<IFieldNormalItem> ( Bose에 해당) ( Person에 해당).

+0

일반 메서드를 만들려면 클래스를 generic으로 만드는 것이 옳습니까? –

+0

나는 이것을 "이것은 단순한 필드를 가진 사람"vs "이것은 정상적인 필드를 가진 사람"이라고 생각합니다. 이 경우 실제로 사람을 차별화하고 클래스 범용을 허용 할 수 있습니다. – tvanfosson

+0

불행히도 yapiskan이 옳습니다. 이 경우 전체 수업을 일반화 할 수는 없습니다 (예 : Person ).해야 할 일이 있습니다. – vdhant

0

아래 코드는 무시할 수 있습니다. 유형 T는 이미 기본 클래스 Person에서 IFieldSimpleItem에 의해 구현 될 필요가 있음을 나타냅니다.

public class Bose : Person 
{ 
    public override T Create<T>() 
     // where T : IFieldNormalItem // You don't need this line. 
    { 
     return default(T); 
    } 
} 

편집 : 위의 코드를이 사건을 해결하지 않도록 내가 완전히 잘못 질문이 있어요. 당신이해야 할 유일한 일은; Create 메서드를 "override"하지만 "virtual"로 재정의하지 마십시오.

public class Bose : Person 
{ 
    public virtual T Create<T>() 
     where T : IFieldNormalItem 
    { 
     return default(T); 
    } 
} 
+0

그러나 그것은 작성에 의해 적어도 IFieldSimpleItem의 사용을 강요합니다. 문제는 뭔가가 Bose에서 상속 받고 IFieldSimpleItem의 implmentation을 반환하는 무언가로 create를 재정의하는 경우입니다. bose가 IFieldNormalItem 이상이어야하므로 오류가 발생합니다. – vdhant

+1

질문이 잘못되었습니다. 그건 그렇고 내 대답을 업데이 트했습니다. –

1

메서드의 정의를 변경할 수는 없지만 Create 메서드 대신 클래스를 만들 수 있습니까?

public class Person<T> where T : IFieldSimpleItem 
{ 
    public virtual T Create() 
    { 
     return default(T); 
    } 
} 

public class Bose<T> : Person<T> where T : IFieldNormalItem 
{ 
    public override T Create() 
    { 
     return default(T); 
    } 
} 
+0

불행히도 나는 이것을 할 수 없다. 나는 방금 제네릭의 스타일을 제 코드에서 제거하는 데 시간을 보냈습니다. 이 경우 전체 클래스를 generic으로 만들 수는 없습니다 (예 :Person ) 내가 할 일이있어서. – vdhant

0

가장 간단한 예는 다형성을 깨뜨리는 것입니다. Person 컬렉션이있는 경우 하나 이상의 항목이 Bose 유형 인 경우 Bose에 도달하는 즉시 충돌이 발생합니다.

Person[] people; 
[...initialize this somewhere...] 

foreach(Person p in people) 
    p.Create<IFieldSimpleItem>(); 
+0

왜 추락할까요? –

1

일반 제약 조건을 변경하면 가상 서명을 재정의하는 경우 허용되지 않는 메소드 서명이 변경됩니다.

나는 별도의 클래스로 만들기 방법을 분할해야 할 수 있습니다 생각 : 허용하지 않는다

public interface IFieldSimpleItem { } 

public interface IFieldNormalItem : IFieldSimpleItem{ } 

public interface IFieldCreator<TField, TPerson> where TField : IFieldSimpleItem where TPerson : Person 
{ 
    TField Create(TPerson person); 
} 

public class Person 
{ 
} 

public class Bose : Person 
{ 
} 

public class PersonFieldCreator : IFieldCreator<IFieldSimpleItem, Person> 
{ 
    public IFieldSimpleItem Create(Person person) { return null; } 
} 

public class BoseFieldCreator : IFieldCreator<IFieldNormalItem, Bose> 
{ 
    public IFieldNormalItem Create(Bose person) { return null; } 
} 
+0

문제는 @vdhant가 Bose의 하위 클래스를 만들려고한다는 제약이 있습니다. 적어도 IFieldNormalItem 인 형식을 만듭니다. –

+0

이 예제에서는 Bose에서 파생 된 클래스에 대해 BoseFieldCreator가 작동합니다. –

2

그것이리스 코프 치환 원칙을 위반하기 때문이다.

public interface IFieldSuperItem : IFieldSimpleItem 

그런 다음이

Person p = new Boss(); 
p.Create<IFieldSuperItem>(); 

초 회선의 통화, 분명히하면서 사람에 만들기의 정의와 호환되지만 호환되지 할 수 있습니다

의 당신이 다른 인터페이스를 가지고 있다고 가정 해 봅시다 Boss (IFieldNormalItem 및 해당 하위 클래스에서만 작동 함)에 정의되어 있습니다.

관련 문제