2008-11-12 28 views
5

Aşağıdaki neden işe yaramadığını anlayamıyorum, herhangi bir fikir? kamu arayüzü IFieldSimpleItem {}"Açık arabirim uygulaması için kısıtlamalar ..."

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); 
    } 
} 

yapıyorum sebebi nedeniyle bir geliştirici Bose devraldığı takdirde, Bose IFieldNormalItem en azından olmanın oluşturma olmak örneği dayanıyor olmasından etmektir. Aşağıda, sadece IFieldSimpleItem olmakla birlikte, yukarıdakiler en azından IFieldNormalItem olmasını zorlamalıdır.

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

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

Alkış Anthony

cevap

2

Sana kadarıyla bazı çalışma zamanı kontrolleri kaydetmek için derleyici ve eşdeğer ilaç kullanımıyla olarak şans bitti eminim. Önceden var olmayan bir şeyi geçersiz kılabilirsiniz ve aynı yöntemlere farklı dönüş türlerine sahip olamazsınız.

Motivasyonunuzu tamamen anladığımı söyleyemem ama teknik değeri var.

İlk denemem, Sanal olmayan bir genel arabirime sahip temel sınıfı kullanıyordu ve daha sonra, zincirdeki herhangi bir şeyin, temel sınıf Oluşturma çağrılmadan önce türü denetlemesi için başka bir korunmuş sanal yönteme sahip CheckCreatedType kullanıyordu.

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."); 

    } 
} 

Aşağıdakiler, temel sınıftaki çalışma zamanı denetiminde yapışır.Çözülemeyen sorun, hala çağrılan temel sınıf yöntemine güvenmeniz gerektiğidir. Hatalı bir alt sınıf, tüm denetimleri base.CheckCreatedType(item) numaralı telefonu arayarak kesebilir.

Alternatifler, temel sınıftaki (alt) tüm alt sınıflar için tüm denetimleri sabitler veya denetimi başka şekilde dışlar.

Deneme 2: (Alt) Sınıflar ihtiyaç duydukları çekleri kaydederler.

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(); 
    } 
} 

onay taban sınıfta çağırmak için bir temsilci kaydederek alt sınıf tarafından yapılır, ancak açık tüm kuralları bilerek temel sınıf olmadan edilir. Yine de, temel sınıfın bunları geri göndermeden önce sonuçları kontrol etmesine izin veren sanal olmayan genel arabirim olduğuna dikkat edin.

Yakalamayı denediğiniz geliştirici hatası olduğunu varsayalım. Geçerliyse, sürüm sürümünün System.Diagnostics.Conditional("DEBUG")] ile yapışmasına izin vererek Sürüm sürümünün çekleri atlamasına izin verebilirsiniz.

Jenerik bilgim mükemmel değil, bu yüzden gereksizdir. Ancak, buradaki kontrollerin tek başına yazılması gerekmemektedir: Bu, başka kullanımlar için uyarlanabilir. Örneğin. Register.. içinden geçen temsilci, yalnızca belirli bir tür referansı kontrol etmek zorunda değildir. '

* Yukarıda yazılan tür adında sözlüğü oluşturmak iyi bir şey değildir; Bu çalışma, kullanılan mekanizmayı göstermek için biraz basittir.

+0

Teşekkürler, bu kontrol çalışma zamanında yapılsa bile, bunun en yakın olacağımı düşünüyorum. Cheers – vdhant

1

Sorun Daha önce tanımlanmış yöntemini geçersiz olduğunu düşünüyorum. Bu nedenle, etkin bir şekilde, izin verilmeyen yöntemin tanımını değiştirmeye çalışırsınız. Tek seçeneğiniz ya yeni bir yöntem oluşturmak, örn. veya Kişi sınıfında normal bir alan veya dinamik olarak doğrulama gerçekleştirin. Bu konuda

+0

Bunun iyi olacağını düşündüm çünkü tanımı daha zayıflamıyorum. – vdhant

0

Ne:

public interface IFieldNormalItem : IFieldSimpleItem 
{ } 

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

Şimdi Person<IFieldSimpleItem> olabilir veya Person<IFieldNormalItem> ( Bose tekabül) ( Person karşılık gelir).

+0

Genel bir sınıf oluşturmak için genel jenerik yapmak doğru mu? –

+0

Bu, "Bu, basit alanlara sahip bir kişi" vs "Bu, normal alanlara sahip bir kişidir" düşüncesindeyim. Bu durumda, aslında kişiyi ayırt edersiniz ve sınıf jenerikinin kabul edilebilir olmasını sağlayabilirsiniz. – tvanfosson

+0

maalesef yapiskan haklı. Bu durumda, yapmam gereken bazı dökümler nedeniyle tüm sınıfı genel (yani ) yapamıyorum. – vdhant

0

Aşağıdaki kod geçersiz kılmak için yeterlidir. T tipi, temel sınıftaki Kişi'de IFieldSimpleItem tarafından uygulanması gerektiği için zaten belirtilmiştir.

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

DÜZENLEME: Yukarıdaki kod bu durumda çözmez yüzden Tamamen yanlış sorum var. Yapman gereken tek şey; Oluşturma yöntemini "geçersiz kıl", ancak "sanal" olarak geçersiz kılmama.

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

Ancak bu, yalnızca en az IFieldSimpleItem öğesinin yaratım tarafından kullanımını zorlar. Sorun, Bose'den bir şey miras alır ve IFaseSimpleItem'in bir implmentasyonunu döndüren bir şeyle yaratımı geçersiz kılarsa, bose'un en azından IFieldNormalItem olması gerektiğinden hata yapmasıdır. – vdhant

+1

Sorunu yanlış anladım. Bu arada cevabımı güncelledim. –

1

yöntemin tanımını değiştiremez ancak bunun yerine Oluştur Yöntemi dersleriniz jenerik yapabilirsiniz görünüyor?

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

maalesef bunu yapamam. Ben sadece jeneriklerin tarzını kodumdan kaldırarak geçirdim. Bu durumda, tüm sınıfı genel yapamayız (örn.Yapmam gereken bazı dökümler yüzünden numaralı kişi. – vdhant

0

En basit örnek, bunun polimorfizmi kırmasıdır. Bu öğelerden bir veya daha fazlasının Bose türünde olduğu bir Kişi koleksiyonunuz varsa, bu bir Bose'e çarptığında en üste çarpılırdı.

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

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

Neden çöktü? –

1

Genel kısıtlamayı değiştirmek, bir sanal noktayı geçersiz kılıyorsanız izin verilmeyen yöntem imzasını değiştirir.

Ben ayrı bir sınıfa oluşturma yöntemini bölmeniz gerekebilir düşünüyorum: izin verilmez

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

Sorun, @vdhant'ın Bose alt sınıflarını sağlamak istediği bir kısıtlama var En az bir IFieldNormalItem –

+0

olan bir türde oluşturun. Bu örnekte, BoseFieldCreator, Bose'den türetilen sınıflar için çalışır. –

2

o Liskov değiştirme prensibi ihlal ettiği.

public interface IFieldSuperItem : IFieldSimpleItem 

Daha sonra bu

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

ikinci satırında çağrı, besbelli iken Kişi Oluştur tanımına uyumlu ama uyumlu değil yapabilir:

Diyelim ki başka bir arayüze var diyelim Patronda tanımlanan (sadece IFieldNormalItem ve onun alt sınıfı ile çalışan).

İlgili konular