2014-01-18 4 views
16

이것은 클래스 디자인 질문입니다.일반 인터페이스 상속 일반 - C#

는 난 그냥 메인 추상 클래스에서 제네릭 형식 T를 지정 피하기 위해 제네릭이 아닌 인터페이스 IRestriction를 사용할 필요가

public abstract class AbstractBlockRule 
{ 
    public long Id{get;set;} 
    public abstract List<IRestriction> Restrictions {get;}; 
} 

public interface IRestriction{} 

public interface IRestriction<T>:IRestriction where T:struct 
{ 
    T Limit {get;} 
} 

public TimeRestriction:IRestriction<TimeSpan> 
{ 
    public TimeSpan Limit{get;set;} 
} 

public AgeRestriction:IRestriction<int> 
{ 
    public int Limit{get;set;} 
} 

public class BlockRule:AbstractBlockRule 
{ 
    public virtual List<IRestriction> Restrictions {get;set;} 
} 

BlockRule rule=new BlockRule(); 
TimeRestriction t=new TimeRestriction(); 
AgeRestriction a=new AgeRestriction(); 

rule.Restrictions.Add(t); 
rule.Restrictions.Add(a); 

주요 추상 클래스를 가지고있다. 저는 제네릭을 처음 접했습니다. 어떤 사람이이 일을 더 잘 디자인하는 방법을 알려줄 수 있습니까?

+1

코드를 개선하기가 어렵습니다.비 제네릭 기본 인터페이스는 원할 경우 'ValueType BoxedLimit {get;}'속성을 가질 수 있지만 유용하다고 확신하지는 않습니다. 결국, 당신은 왜 다른 타입의 객체들의'List <>'가 필요합니까? –

+0

(C#과 .NET은 값 타입의 공분산을 지원하지 않기 때문에, 일반적인 형태의 공용 변수 인 'IRestriction '을 값 유형 (구조체)과 함께 사용하는 것은 당연한 생각입니다. –

+0

I 왜 당신이 추상 클래스 나 메소드를 만들지 않는지 모르겠다. '제한'은 최소 또는 최대입니까? 그렇다면 아마'IComparable'이 더 유용 할 것입니다. –

답변

12

귀하의 접근 방식은 일반적입니다 (예 : IEnumerable <T>은 이와 같이 IEnumerable을 구현합니다). 코드의 소비자에게 최대의 유틸리티를 제공하려는 경우 비 제네릭 인터페이스에서 비 제너릭 접근자를 제공 한 다음 일반 구현에서이를 숨길 수 있습니다. 예 :

public abstract class AbstractBlockRule 
{ 
    public long Id{get;set;} 
    public abstract List<IRestriction> Restrictions { get; set; } 
} 

public interface IRestriction 
{ 
    object Limit { get; } 
} 

public interface IRestriction<T> : IRestriction 
    where T:struct 
{ 
    // hide IRestriction.Limit 
    new T Limit {get;} 
} 

public abstract class RestrictionBase<T> : IRestriction<T> 
    where T:struct 
{ 
    // explicit implementation 
    object IRestriction.Limit 
    { 
     get { return Limit; } 
    } 

    // override when required 
    public virtual T Limit { get; set; } 
} 

public class TimeRestriction : RestrictionBase<TimeSpan> 
{ 
} 

public class AgeRestriction : RestrictionBase<TimeSpan> 
{ 
} 

public class BlockRule : AbstractBlockRule 
{ 
    public override List<IRestriction> Restrictions { get; set; } 
} 

여기에도 기본 제한 클래스를 사용했으나 필수는 아닙니다.

0

인터페이스는 계약을 구현하는 엔티티가 따라야하는 계약입니다. 당신은 최대한 멀리 볼 수 IRestriction

, 당신은 기본적으로 무엇을해야 할 수도 같은 이름을 가진 두 개의 계약을 만들었습니다

IRestriction 제네릭이 아닌 인터페이스를 구현해야합니다 제한 할 수있는 클래스에 대한 플래그입니다 .

두 번째 인터페이스는 제한 속성을 포함하는 제한된 개체 인 것으로 보입니다. 따라서 두 번째 IRestriction 인터페이스의 정의는 ILimitRestriction이거나 비즈니스 요구에 맞는 이름이 될 수 있습니다.

는 따라서 ILimitRestriction 여전히 IRestriction

public abstract class AbstractBlockRule 
{ 
    public long Id{get;set;} 
    public abstract List<IRestriction> Restrictions {get;}; 
} 

public interface IRestriction{} 

public interface IRestrictionWithLimit<T>:IRestriction where T:struct 
{ 
    T Limit {get;} 
} 

public TimeRestriction:IRestrictionWithLimit<TimeSpan> 
{ 
    public TimeSpan Limit{get;set;} 
} 

public AgeRestriction:IRestrictionWithLimit<int> 
{ 
    public int Limit{get;set;} 
} 

public class BlockRule:AbstractBlockRule 
{ 
    public virtual List<IRestriction> Restrictions {get;set;} 
} 
2

런타임 취급 IRestriction<TimeSpan>IRestriction<int> 서로 다른 별개의 클래스의 객체 ILimitRestriction을 상속 클래스를 표시 할 것이다 IRestriction에서 상속 할 수 있습니다 (그들은 심지어 정적 변수의 자신의 세트를 가지고). 귀하의 경우 상속 계층 구조에서 IRestriction<TimeSpan>IRestriction<int>에 공통적 인 클래스는 모두 IRestrictionobject입니다.

실제로 실제로는 IRestriction의 목록을 가지고있는 것이 합리적입니다. 보조 노트로


: 당신은 당신이 상관없이 IRestriction<TimeSpan> 또는 IRestriction<int> 상대하고 있는지 여부에 액세스 할 수 있다는 거기에 속성 Limit 있습니다. 이 경우에 수행 할 작업은 object Limit { get; }IRestriction에 정의하고 실제 구현에서 숨기는 것입니다. 이처럼 :

public interface IRestriction 
{ 
    object Limit { get; } 
} 

public interface IRestriction<T> : IRestriction 
    where T : struct 
{ 
    new T Limit { get; set; } 
} 

public class TimeRestriction : IRestriction<TimeSpan> 
{ 
    public TimeSpan Limit { get; set; } 

    // Explicit interface member: 
    // This is hidden from IntelliSense 
    // unless you cast to IRestriction. 
    object IRestriction.Limit 
    { 
     get 
     { 
      // Note: boxing happens here. 
      return (object)Limit; 
     } 
    } 
} 

당신은 그것이 어떤 종류의 상관하지 않습니다 때 모든 IRestrictionobjectLimit에 액세스 할 수 있습니다이 방법. 예 :

foreach(IRestriction restriction in this.Restrictions) 
{ 
    Console.WriteLine(restriction.Limit); 
}