Soru Arabirimler, Kalıtım, Kapalı işleçler ve tür dönüşümleri, neden bu şekilde?


DDay ICal adında bir sınıf kütüphanesi ile çalışıyorum. Outlook Takvimlerinde uygulanan iCalendar Sistemi ve birçok başka sistem için bir C # sarmalayıcısıdır. Sorum şu, bu sistemle yaptığım bazı işlerden kaynaklanıyor.

Burada söz konusu 3 nesne var

  • IRecurrencePattern - Arayüz
  • RecurrencePattern - IRecurrencePattern Arayüzünün Uygulanması
  • DbRecurPatt - Kapalı tür operatöre sahip Özel Sınıf

IRecurrencePattern: Tüm kodlar gösterilmiyor

public interface IRecurrencePattern
{
    string Data { get; set; }
}

RecurrencePattern: Tüm kodlar gösterilmiyor

public class RecurrencePattern : IRecurrencePattern
{
    public string Data { get; set; }
}

DbRecurPatt: Tüm kodlar gösterilmez

public class DbRecurPatt
{
    public string Name { get; set; }
    public string Description { get; set; }

    public static implicit operator RecurrencePattern(DbRecurPatt obj)
    {
        return new RecurrencePattern() { Data = $"{Name} - {Description}" };
    }
}

Kafa karıştırıcı kısım: DDay.ICal sistem üzerinden kullanıyorlar IListtakvimde her olay için bir Yineleme kalıpları koleksiyonu içerecek şekilde, özel sınıf bir veritabanından bilgi almak için kullanılır ve daha sonra örtülü tip dönüşüm işleciyle Nükset Örüntüsüne dökülür.

Ama kodda, dönüştürürken kilitlenmeye devam ettiğini fark ettim. List<IRecurrencePattern> bir List<DbRecurPatt> Değiştirmem gerektiğini anladım RecurrencePatternve sonra dönüştürün IRecurrencePattern (uygulayan diğer sınıflar olduğu gibi IRecurrencePattern farklı olarak koleksiyonda yer almaktadır

var unsorted = new List<DbRecurPatt>{ new DbRecurPatt(), new DbRecurPatt() };
var sorted = unsorted.Select(t => (IRecurrencePattern)t);

Yukarıdaki kod çalışmıyor, hata veriyor IRecurrencePattern.

var sorted = unsorted.Select(t => (IRecurrencePattern)(RecurrencePattern)t);

Bu işe yarıyor, bu yüzden sahip olduğum soru şu; İlk olan neden çalışmıyor? (Ve bu yöntemi geliştirmenin bir yolu var mı?)

Ben örtülü operatör üzerinde olduğu için olabilir inanıyorum RecurrencePattern Nesne ve arayüz değil, bu doğru mu? (Arabirimler ve örtük operatörler için yeniyim)


21
2017-08-26 12:35


Menşei


bir List<DbRecurPatt> gerçek bir nesnedir. Onu bir şeye atamazsın List<IRecurrencePattern> çünkü birisi o zaman olmayanDbRecurPatt bunun içine. - Damien_The_Unbeliever
Bir oyuncudan not edilmelidir. RecurrencePattern için IRecurrencePattern aynı nesneyi gösterir, ancak DbRecurPatt için RecurrencePattern Tamamen yeni bir nesne oluşturur. Yani, bir arabirim olarak başvurmadan önce yeni nesneyi oluşturmasını söylemeniz gerekir. - juharr


Cevaplar:


Derleyiciden bunu yapmasını istediniz:

  1. Bu bende var: DbRecurPatt
  2. Bunu istiyorum: IRecurrencePattern 
  3. Lütfen 1. noktadan 2. noktaya gelmenin bir yolunu bulun.

Derleyici, yalnızca bir seçeneği olsa bile, bunu yapmanıza izin vermez. Cast operatör özellikle bunu söylüyor DbRecurPatt bir dönüştürülebilir RecurrencePatterndeğil IRecurrencePattern.

Derleyici, yalnızca, ilgili iki türden birinin birinden diğerine nasıl dönüştürüleceğine dair bir kural belirlediğini kontrol eder, ara adımlara izin vermez.

İzin verilen bir operatör tanımlanmadığından DbRecurPatt doğrudan dönüştürülecek IRecurrencePattern, derleyici bunu bir hard-cast olarak derler, referansı referans olarak bir arayüz üzerinden yeniden yorumlayarak çalışma zamanında başarısız olur.

Yani, bir sonraki soru şuydu: Bunu nasıl yapabilirim? Ve cevap, yapamazsın.

Derleyici, kullanıcı tanımlı bir dönüşüm işlecini bir arabirime veya bir arabirime tanımlamanıza izin vermez. Stack Overflow hakkında farklı bir soru daha fazla bilgiye sahiptir.

Böyle bir operatör tanımlamaya çalışırsanız:

public static implicit operator IRecurrencePattern(DbRecurPatt obj)
{
    return new RecurrencePattern() { Data = $"{obj.Name} - {obj.Description}" };
}

Derleyici şunu söyleyecektir:

CS0552
  'DbRecurPatt.implicit operator IRecurrencePattern (DbRecurPatt)': bir arabirime veya kullanıcı arabirimine göre kullanıcı tanımlı dönüşümlere izin verilmiyor


16
2017-08-26 12:53



Sanırım sen ayrıldın I örtülü kullanıcı kodu örneğinde arabirim adının dışında. - juharr
Bu ifadenin son kısmı; Demek istediğin return new IRecurrencePattern() yerine return new RecurrencePattern çünkü bu kodu denedikten sonra çalışır? - Alec Scratch
@AlecScratch Hayır, imzanın var olması gerektiğine inanıyorum public static implicit operator IRecurrencePattern(DbRecurPatt obj) - juharr
Tamam, şimdi onu görüyorum. Zaten benim için mantıklı değil çünkü bir ara yüz oluşturabilirsin. Derp. - Alec Scratch
Evet, imzamda bir eksiklik vardı, örnek LINQPad programımdan yanlış yöntemi kopyaladım, ancak yöntemin içindeki sınıfı kullanmak istediğim doğru. - Lasse Vågsæther Karlsen


İlk olan neden çalışmıyor?

Çünkü çalışma zamanını soruyorsun iki örtülü dönüşümler - biri RecurrencePattern ve bir tane IRecurrencePattern. Çalışma zamanı sadece bir direkt örtülü bir ilişki - gitmesini istemek için tüm olası yolları taramayacaktır. Varsayalım çoklu uygulayan farklı sınıf türlerine örtülü dönüşümler IRecurrencePattern. Çalışma zamanı hangisini seçer? Bunun yerine, bireysel atmalarını belirtmeye zorlar.

Bu C # Dil belirtiminin 6.4.3 Bölümünde belgelenmiştir:

Kullanıcı tanımlı bir dönüşümün değerlendirilmesi, birden fazla kez içermez   kullanıcı tanımlı veya yükseltilmiş dönüşüm operatörü. Başka bir deyişle   S tipinden T'ye dönüşüm, hiçbir zaman ilk önce bir   kullanıcı tanımlı S'den X'e dönüştürme ve ardından kullanıcı tanımlı çalıştır   X'den T.'ye dönüşüm


6
2017-08-26 12:52





Diğerleri zaten işaret ettiği gibi, doğrudan yapamazsınız atlama itibaren DbRecurPatt için IRecurrencePattern. Bu yüzden bu çirkin çifte kadroyla sonuçlanacaksın:

var sorted = unsorted.Select(t => (IRecurrencePattern)(RecurrencePattern)t);

Ancak, tamlık için, bir yerden çıkmanın mümkün olduğu belirtilmelidir. DbRecurPatt bir IRecurrencePattern Mevcut tasarımınızla herhangi bir döküm olmadan. Bunu yapmak için, ifadenizi birden çok ifadeye bölmeniz gerekir ve bunu yaparak, kod oldukça çirkin hale gelir.

Yine de, bunu bilmek güzel kutu bunu oyuncular olmadan yap:

var sorted = unsorted.Select( t => {
    RecurrencePattern recurrencePattern = t; // no cast
    IRecurrencePattern recurrencePatternInterface = recurrencePattern; // no cast here either
    return recurrencePatternInterface;
});

DÜZENLE

Bill Nadeau'nun bu fikre verdiği cevaptan. Ayrıca, örtülü dönüştürme ve derleme zaman garantilerinden yararlanarak, kodu bu şekilde yazarak, bu şekilde yazabilirsiniz:

var sorted = unsorted
    .Select<DbRecurPatt, RecurrencePattern>(t => t) // implicit conversion - no cast
    .Select<RecurrencePattern, IRecurrencePattern>(t => t); // implicit conversion - no cast

4
2017-08-26 13:18



Yanıt için teşekkürler, bir soru olsa da, bu döküm yönteminden daha iyi olup olmadığını biliyor musunuz? Veya bu tamamen "bilmek güzel" mi? - Alec Scratch
Tek bir avantaj var (kişisel olarak büyük bir fikir olduğunu düşünüyorum): Eğer kodları herhangi bir kod olmadan derleyebilirseniz, dönüşümün aniden çalışma zamanında başarısız olmayacağından emin olabilirsiniz. Karşınızdaki ifadeyle (IRecurrencePattern)t, derleme yaptı, ancak çalışma zamanında başarısız oldu. Ancak, kodun çirkin ve daha ayrıntılı olduğu inkar edilemez. - sstan
Orada hâlâ iki oyuncu var, onlar sadece bir türden bir değişkenden referansı başka bir türün değişkenine kopyaladığınızdan dolaylı olarak ima ediyorlar. Yani "yok oyuncular" demek doğru değil. - D Stanley
@D Stanley: Ne dediğini görüyorum. Ama bence bir fark var. oyuncular ve bir dönüştürme. Hayır demedim mi dönüştürme gerekliydi, yanlış olacağını sana katılıyorum. Terminolojinin standart dokümanlar içinde bu şekilde kullanıldığı örnekler için İşte ("Açık olarak bildirilen dönüşümler, çağrılacak bir döküm gerektiriyor."), İşte ("örtülü dönüşüm - yayın gerekmez") veya İşte - sstan
Yaşasın! Benim fikrim gibi insanlar! Bence OP'nin durumu için iki tekniğin birleşimi en iyi ve en şık olacak. Tüm dürüstlükte, sert oyunculardan kaçınıldığı sürece OP iyi olmalıdır. - Bill Nadeau


Ne istediğini başarmanın başka bir yolu var. Genel argümanlarınızı, derleyicinizin genel argümanlarınızdan çıkarmasına izin vermek yerine, yöntem çağrılarınızda belirtin. Hala dökümden kaçınacaksınız ve diğer seçeneklerden biraz daha az ayrıntılı olabilir. Tek uyarı, eğer önemliyse, listenizi çözecek ek bir Linq ifadesi eklemeniz gerekir.

var sorted = unsorted
   .Select<DbRecurPatt, RecurrencePattern>(t => t)
   .ToList<IRecurrencePattern>();

Ekstra Linq ifadesinden kaçınmak için bu cevabı sstan ile birleştirebilirsiniz.


2
2017-08-26 18:33



Fikrini çok beğendim. Fikrini ödünç aldım ve cevabım için bir ayarlama yaptım. Temel olarak, zincir 2 olurdum Select yöntem, kodun OP'nin orijinal kod snippet'ini eşdeğer tutarken dökümden kaçınılmasını gerektirir. Sonuç oldukça temiz. - sstan


... ve örtülü operatörle ilgili son sorunuzu yanıtlamak için - hayır, bir arabirimde örtülü bir operatör tanımlayamazsınız. Bu konu bu konuda daha ayrıntılı olarak ele alınmıştır:

arayüzleri kullanarak örtülü operatör


1
2017-08-26 12:54