2010-11-09 17 views
9

BenBu co/contravariance olayını gerçekten anlamıyorum ... Hem jenerik hem de set yöntemlerine sahip olamıyorum?

interface IRepo<out T> where T: IModel { 
} 

Güzel, çalışıyor ..

interface IModel {} 

class MyModel : IModel {} 

interface IRepo<T> where T: IModel { 
} 

class Repo : IRepo<MyModel> { 
} 

// Cannot implicitly convert.. An explicit convertion exists. Missing cast? 
IRepo<IModel> repo = new Repo(); 

yüzden kovaryansını ihtiyacım .. Bazı örneklerle sorunlarımı anlatacağım düşünüyorum. Sonra kullanmak istiyorum:

interface IRepo<out T> where T: IModel { 
    T ReturnSomething(); 
} 

class Repo : IRepo<MyModel> { 
    public MyModel ReturnSomething() { return default(MyModel); } 
} 

Tüm iyi, ancak repo da nesneler eklemek gerekiyor.

// Invalid variance: The type parameter 'T' must be contravariantly valid on 'IRepo<T>.InsertSomething(T)'. 'T' is covariant. 
interface IRepo<out T> where T: IModel { 
    T ReturnSomething(); 
    void InsertSomething(T thing); 
} 

class Repo : IRepo<MyModel> { 
    public MyModel ReturnSomething() { return default(MyModel); } 
    public void InsertSomething(MyModel thing) { } 
} 

Yani iki parametre eklemeye çalıştığınızda: bir out parametresi ile, bunu yapamaz

interface IRepo<out TReturn, TInsert> 
    where TReturn : IModel 
    where TInsert : IModel 
{ 
    TReturn ReturnSomething(); 
    void InsertSomething(TInsert thing); 
} 

Ve ilk örnekte olduğu gibi aynı hatayı alıyorum. in TInsert

Aynı hata iletisini aldığımda, hem yerleştirmeyi hem de getirmeyi nasıl destekleyebilirim?

DÜZENLEME: Ben olası bir çözüm bulduk, ama bu kadar optimum

interface IRepo<out TResult> where TResult : IModel { 
    TResult ReturnSomething(); 
    // I need to duplicate my constraint here.. 
    void InsertSomething<TInsert>(TInsert thing) where TInsert : IModel; 
} 


class Repo : IRepo<MyModel> { 
    public MyModel ReturnSomething() { return default(MyModel); } 
    // ... And here 
    public void InsertSomething<T>(T thing) where T: IModel { } 
} 

EDIT2 den: Eric cevaben: Bu ben ne daha tam örnektir elde etmeye çalışıyorum. Kovaryansı gerçekten çok istiyorum, böylece IRepo örneklerini gruplayabiliyorum ve yine de modeli örnek olarak kullanarak ekleme/güncelleştirme yöntemlerine sahip olmasını istiyorum. Öğeleri eklemek için derleme zamanı türü güvenliği alamıyorum, ancak bu kullanım durumu için yalnızca öğeleri okumam gerekiyor.

interface IModel { } 
class SomeModel : IModel { } 
class OtherModel : IModel { } 

interface IRepo<T> 
{ 
    T ReturnSomething(); 
    void AddSomething(T thing); 
} 

interface ISubRepo<T> : IRepo<T> where T : IModel { } 

class SomeSubRepo : ISubRepo<SomeModel> { 
    public SomeModel ReturnSomething() { return default(SomeModel); } 
    public void AddSomething(SomeModel thing) { } 
} 

class OtherSubRepo : ISubRepo<OtherModel> { 
    public OtherModel ReturnSomething() { return default(OtherModel); } 
    public void AddSomething(OtherModel thing) { } 
} 

class Program { 
    static void Main(string[] args) 
    { 
     ISubRepo<IModel>[] everyone = new ISubRepo<IModel>[] { 
      new SomeSubRepo(), 
      new OtherSubRepo() 
     }; 

     WorkOnAll(everyone); 
    } 

    static void WorkOnAll(IEnumerable<ISubRepo<IModel>> everyone) 
    { 
     foreach(ISubRepo<IModel> repo in everyone) { 
      IModel model = repo.ReturnSomething(); 
      // Etc. 
     } 
    } 
} 
+0

Sorunun ne olduğunu tam olarak anlamakta zorlanıyorum. Herhangi bir kod parçasının neden çalışmadığını soruyor musunuz? Eğer öyleyse, neden bu biraz farklı bir davranış göstermesi gerektiğine inandığınız kısa bir açıklama ile birlikte kod verebilir misiniz? –

+0

@Eric: Daha eksiksiz bir örnek ekledim. Umarım bu biraz açıklar. – simendsjo

cevap

2

Ben en iyi bahis ikiye sizin arayüzünü bölmek olduğunu düşünüyorum Repo örneklerini içeren bir List<IReadableRepo<IModel>> oluşturabilirsiniz.

+0

Çok güzel! Hatta bir IRepo : IReadRepo , IWriteRepo ve BazıBase : IRepo ve SomeImpl: SomeBase . Çok teşekkür ederim! – simendsjo

+0

Ve bir yan not olarak; Sadece beton sınıfları üzerinde çalışması gerekiyor gibi ben sadece , yazılabilir repo istemiyoruz – simendsjo

2

Aynı türde bir nesneyi eklemek ve döndürmek isterseniz, değişime gereksiniminiz vardır. Değişkeninizi bunu aklınızda bulundurmanız yeterlidir.

IRepo<MyModel> repo = new Repo(); 

Edit:

Sadece repo değişkeni bildirmek için bu hattı ile ilk pasajı kullanmak Gerekli kod yazmak için gerekli 10 dakika almış. Ben o kod bit (Visual C# Express) bilgisayarımda derler emin olabilirsiniz:

interface IReadableRepo<out T> where T : IModel 
    { 
     T ReturnSomething(); 
    } 

    interface IWritableRepo<in T> where T : IModel 
    { 
     void InsertSomething(T thing); 
    } 

    class Repo : IReadableRepo<MyModel>, IWritableRepo<MyModel> 
    { 
     ... 
    } 

Asistan:

public interface IModel { 

} 

public interface IRepo<T> where T : IModel { 
    T returnModel(); 
    void putModel(T model); 
} 

public class MyModel : IModel { 

} 

public class Repo : IRepo<MyModel> { 
} 

public static class Program { 
    void main() { 
     IRepo<MyModel> repo = new Repo(); 
     var model = new MyModel(); 
     repo.putModel(model); 
     var model2 = repo.returnModel(); 
    } 
} 
+0

Örneklerimi denemeniz gerekecek. – simendsjo

+0

Ah, derleme değil. IModel yerine MyModel kullandığını farketmedin. Bunun sebebi de, IModel.Ben bir IEnumerable > yere saklamak istediğiniz ve benim hizmet fabrika kullanarak bu oluşturmak istiyorum: Ben cevap görebiliyordu önce ServiceFactory.CreateAll > ... – simendsjo

+0

Maalesef düzenleme geldi. –

İlgili konular