2012-01-18 21 views
10

aşağıdaki kod parçacığı kovaryant değil neden olarak anlamış değilim?C# dışı genel tür parametreleri neden kovaryansı ihlal ediyor?

public interface IResourceColl<out T> : IEnumerable<T> where T : IResource { 

    int Count { get; } 

    T this[int index] { get; } 

    bool TryGetValue(string SUID, out T obj); // Error here? 
    } 

Hata 1 Geçersiz varyans: tür parametresi 'T 'IResourceColl.TryGetValue (T üzerinden dizesi)' üzerine değişmez geçerli olması gerekir. 'T', kovaryantıdır.

Benim arayüzü yalnızca çıkış pozisyonlarda şablon parametresini kullanır. Kolayca

public interface IResourceColl<out T> : IEnumerable<T> where T : class, IResource { 

    int Count { get; } 

    T this[int index] { get; } 

    T TryGetValue(string SUID); // return null if not found 
    } 

gibi bir şey bu kodu refactor olabilir ama benim asıl kod aslında kovaryansını ihlal ederse anlamaya çalışıyorum ya bu kovaryans bir derleyici veya .NET sınırlama ise.

+0

olası yinelenen [C#: destek polimorfizmi 'dışarı' değil neden 'ref' ve] (http: // stackoverflow. com/questions/1207144/c-sharp-why-does-ref-ve-out-destek-polimorfizm) – Jon

+0

Burada dikkat edilmesi gereken önemli nokta şudur: ['out'] (http://msdn.microsoft.com/en -us/library/ee332485.aspx) (parametre değiştirici) ['out'] ile tamamen ilgisi yoktur (http://msdn.microsoft.com/en-us/library/dd469487.aspx) (jenerik türde kullanılır) parametre). – Jon

+0

@Jon - bu soru C# 3.0 ve öncesi için geçerlidir. Burada açıklanan sözdizimi C# 4.0 – Oded

cevap

11

sorun aslında buradadır:

bool TryGetValue(string SUID, out T obj); // Error here? 

Bunu Eğer bir örneği size beri, bu nedenle bildirdiğinden olamazobj hem pas geçiyoruz olsa yine gelir out parametre olarak obj işaretlenmiş T'u yazın ve geri gönderin.

Düzenleme:

o T "out" olarak işaretlenmiş yapmak yasal olmalı:

Eric Lippert'ın kimseye ben his answer to "ref and out parameters in C# and cannot be marked as variant" bakın ve out parametreleri açısından onu alıntı daha iyi diyor ? Ne yazık ki hayır. "dışarı" aslında sahnelerin arkasında "ref" den farklı değildir. "Out" ve "ref" arasındaki tek arasındaki fark, derleyicinin denetiminin, callee tarafından atanmadan önce bir çıkış parametresinden okumasını ve normalde kimliğinin, callee normale dönmeden önce atama gerektirdiğidir. C# dışında bir .NET dilinde bu arabirimi uygulaması yazdım Birisi başlatıldı önce öğeden okumak mümkün olacaktır ve bu nedenle bir girdi olarak kullanılabilir. Biz nedenle bu durumda olarak "out" olarak T işaretleme korusun. Bu pişmanlık verici, ama yapabileceğimiz hiçbir şey yok; CLR'nin güvenlik kurallarına uymak zorundayız. çıkış parametrelerine sağlanan değer tam çıkış parametresi beyanı ile aynı tip olmalıdır çünkü

+0

Bir T'den başka bir şeyin örneğini iletebilirim, ancak C# kuralları, okuyabilmek için önce bir şey atamam gerekiyor, bu yüzden asla bir soruna neden olmaz. – MerickOWA

+0

Şimdi görüyorum, diğer dillerdeki değeri okumak mümkün ve eric'in blogu, C# 'da' kırılabilecek 'durumları bile gösteriyor. Bu, kovaryansın neden ihlal edildiğini açıklıyor. – MerickOWA

+0

Sorun, 'out' parametreleri olarak adlandırılanların gerçekten olmadığı; gerçek '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' Arayan kişi daha sonra uygun alanları yapıdan 'çıkış' parametrelerine otomatik olarak kopyalayacak ve ardından geri kalan alanı (varsa) geri dönüş değeri olarak görecektir. Eğer 'out' parametreleri bu şekilde uygulanmışsa, bunlar gerçekten kovaryant olabilirler. – supercat

1

O kovaryansını ihlal eder. Örneğin, T varsayarak bir dize oldu kovaryans o

var someIResourceColl = new someIResourceCollClass<String>(); 
Object k; 
someIResourceColl.TryGetValue("Foo", out k); // This will break because k is an Object, not a String 
1

bu küçük örneğe bakın yapmaya Tamam olacağını ima edebilir ve bunu izin verilmemesinin sebebi anlayacaksınız:

public void Test() 
{ 
    string s = "Hello"; 
    Foo(out s); 
} 

public void Foo(out string s) //s is passed with "Hello" even if not usable 
{ 
    s = "Bye"; 
} 

out demektir yürütme yöntemini ayrılmadan önce s kesinlikle atanmalıdır ve kesinlikle yöntem vücutta atanana kadar tersine siz s kullanamazsınız.Bu, covariance kurallarıyla uyumlu görünüyor. Ancak hiçbir şey, yöntemi çağırmadan önce çağrı sitesinde s'u atamanızı engeller. Bu değer, kullanılabilir olmasa bile, jenerik tipin yalnızca kovaryans kurallarına aykırı olan yönteme etkili bir şekilde geçirdiğini ifade eden yönteme aktarılır. Bir yöntemin dönüş tipi olarak kullanılır.

3

Uzantı yöntemini kullanarak olası bir çözüm. ille bakış implementor açıdan elverişli, ancak kullanıcı Değil mutlu olmalı:

public interface IExample<out T> 
{ 
    T TryGetByName(string name, out bool success); 
} 

public static class HelperClass 
{ 
    public static bool TryGetByName<T>(this IExample<T> @this, string name, out T child) 
    { 
     bool success; 
     child = @this.TryGetByName(name, out success); 
     return success; 
    } 
} 

public interface IAnimal { }; 

public interface IFish : IAnimal { }; 

public class XavierTheFish : IFish { }; 

public class Aquarium : IExample<IFish> 
{ 
    public IFish TryGetByName(string name, out bool success) 
    { 
     if (name == "Xavier") 
     { 
      success = true; 
      return new XavierTheFish(); 
     } 
     else 
     { 
      success = false; 
      return null; 
     } 
    } 
} 

public static class Test 
{ 
    public static void Main() 
    { 
     var aquarium = new Aquarium(); 
     IAnimal child; 
     if (aquarium.TryGetByName("Xavier", out child)) 
     { 
      Console.WriteLine(child); 
     } 
    } 
} 
İlgili konular