Bir ebeveyn sınıfından bir alt sınıfa geçiş yapmaya çalışıyorum ancak bir InvalidCastException alıyorum. Çocuk sınıfının int türünde yalnızca bir özelliği vardır. Ne yapmam gerektiğini bilen var mı?
Bir ebeveyn sınıfından bir alt sınıfa geçiş yapmaya çalışıyorum ancak bir InvalidCastException alıyorum. Çocuk sınıfının int türünde yalnızca bir özelliği vardır. Ne yapmam gerektiğini bilen var mı?
Yanıtlar:
C # 'da alt yayın yapmanın basit bir yolu, ebeveyni serileştirmek ve ardından onu alt öğe olarak seri durumdan çıkarmaktır.
var serializedParent = JsonConvert.SerializeObject(parentInstance);
Child c = JsonConvert.DeserializeObject<Child>(serializedParent);
Yukarıdaki iki kod satırını kullanarak hayvanı köpeğe dönüştüren basit bir konsol uygulamam var.
Bir memeliyi köpeğe atamazsınız - bir kedi olabilir.
Bir yemeği sandviçin içine atamazsınız - bu bir çizburger olabilir.
Bir arabayı bir Ferrari'ye çeviremezsiniz - bir Honda olabilir veya daha spesifik olarak, bir Ferrari 360 Modena'yı bir Ferrari 360 Challange Stradale'ye çeviremezsiniz - her ikisi de Ferrari 360'lar olsa bile farklı parçalar vardır.
Temel sınıf referansınızın başvurduğu örnek, alt sınıfınızın bir örneği değil. Yanlış bir şey yok.
Daha spesifik olarak:
Base derivedInstance = new Derived();
Base baseInstance = new Base();
Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException
Yayınlamanın başarılı olması için, aşağıya doğru tahmin ettiğiniz örnek, aşağıya tahmin ettiğiniz sınıfın bir örneği olmalıdır (veya en azından aşağıya doğru tahmin ettiğiniz sınıf, örneğin sınıf hiyerarşisi içinde olmalıdır), aksi takdirde döküm başarısız olur.
İnsanların çoğunun açıkça ebeveynden çocuğa rol almanın mümkün olmadığını söylediğini gördüm , bu aslında doğru değil. Gözden geçirilmiş bir başlangıç yapalım ve örneklerle kanıtlamayı deneyelim.
.Net'te bildiğimiz gibi, tüm dökümlerin iki geniş kategorisi vardır.
Referans türünde, herhangi bir senaryonun yatabileceği üç ana durumsal durum vardır.
Vaka 1. Doğrudan veya dolaylı herhangi bir ebeveynin çocuğu
Employee e = new Employee();
Person p = (Person)e; //Allowed
Durum 2. Üst nesneyi tutan üst değişken (İzin verilmez)
Person p = new Person(); // p is true Person object
Employee e = (Employee)p; //Runtime err : InvalidCastException <-------- Yours issue
Durum 3. Alt nesneyi tutan üst değişken (Her Zaman Başarılı)
Not: Nesnelerin polimorfik doğası olduğundan, ana sınıf türündeki bir değişkenin bir alt türü tutması mümkündür.
Person p = new Employee(); // p actually is Employee
Employee e = (Employee)p; // Casting allowed
Sonuç: Her şeyden önce okuduktan sonra, ebeveynden çocuğa dönüşümün nasıl mümkün olduğu gibi şimdi mantıklı olacağını umuyoruz (Durum 3).
Sorunun Cevabı:
Cevabınız 2. durumda. Bu tür bir atamaya OOP tarafından izin verilmediğini ve OOP'nin temel kurallarından birini ihlal etmeye çalıştığınızı görebildiğiniz yerde her zaman güvenli yolu seçin.
Dahası, kullanılarak tavsiye etti .net böyle istisnai durumlardan kaçınmak için gibidir / o haberdar kararlar alıp güvenli dökümü sağlamak için yardımcı olacaktır operatörler.
Böyle bir oyuncu kadrosunun mantıklı olacağı bazı durumlar vardır.
Benim durumum, ağ üzerinden bir BASE sınıfı alıyordum ve bunun için daha fazla özelliğe ihtiyacım vardı. Bu yüzden onu istediğim tüm çan ve ıslıklarla kendi tarafımda işleyecek şekilde türetmek ve alınan BASE sınıfını DERIVED sınıfına aktarmak bir seçenek değildi (Throws InvalidCastException of Course)
Bir pratik düşünce out-of-the-box ÇÖZÜM aslında TABAN sınıfını miras DEĞİLDİ bir YAYIM Yardımcısı sınıf bildirmek için, ama bir üyesi olarak BT DAHİL.
public class BaseExtension
{
Base baseInstance;
public FakeDerived(Base b)
{
baseInstance = b;
}
//Helper methods and extensions to Base class added here
}
Eğer gevşek bir bağlantıya sahipseniz ve GERÇEKTEN mutlak bir türetme ihtiyacına sahip olmadan temel sınıfa birkaç ekstra özelliğe ihtiyacınız varsa, bu hızlı ve basit bir çözüm olabilir.
BaseExtensionen azından IBasebenzer bağlamlarda kullanabileceğiniz şekilde uygulamasını istediğinizi düşünürken haklı mıyım ? Yoksa ihtiyaçlarınız için önemli değil miydi?
Bu, nesneye yönelik ilkeleri ihlal eder. Burada ve projenin başka yerlerinde şık bir çözümün bir projeksiyonu yapılandırmak için AutoMapper gibi bir nesne haritalama çerçevesi kullanmak olduğunu söyleyebilirim .
İşte gerekli olandan biraz daha karmaşık bir yapılandırma, ancak çoğu durumda yeterince esnek:
public class BaseToChildMappingProfile : Profile
{
public override string ProfileName
{
get { return "BaseToChildMappingProfile"; }
}
protected override void Configure()
{
Mapper.CreateMap<BaseClass, ChildClassOne>();
Mapper.CreateMap<BaseClass, ChildClassTwo>();
}
}
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<BaseToChildMappingProfile>();
});
}
}
Uygulama aramayı başlattığında AutoMapperConfiguration.Configure()ve ardından şu şekilde projeksiyon yapabilirsiniz:
ChildClassOne child = Mapper.Map<BaseClass, ChildClassOne>(baseClass);
Özellikler konvansiyonel olarak eşlenir, bu nedenle sınıf miras alınırsa özellik adları tam olarak aynı olur ve eşleme otomatik olarak yapılandırılır. Yapılandırmayı değiştirerek ek özellikler ekleyebilirsiniz. Belgelere bakın .
Paul, 'Yapabilir miyim' diye sormadın - Bunu nasıl yapacağını bilmek istediğini varsayıyorum !
Bunu bir proje üzerinde yapmak zorundaydık - sadece bir kez genel bir şekilde oluşturduğumuz birçok sınıf var, sonra türetilmiş sınıflara özgü özellikleri başlatıyoruz. Örneğim VB'de olduğu için VB kullanıyorum (zorlu noogies), ancak VB örneğini bu siteden çaldım ve daha iyi bir C # sürümüne sahip:
Basit kod:
Imports System
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Text
Imports System.Diagnostics
Module ClassUtils
Public Sub CopyProperties(ByVal dst As Object, ByVal src As Object)
Dim srcProperties() As PropertyInfo = src.GetType.GetProperties
Dim dstType = dst.GetType
If srcProperties Is Nothing Or dstType.GetProperties Is Nothing Then
Return
End If
For Each srcProperty As PropertyInfo In srcProperties
Dim dstProperty As PropertyInfo = dstType.GetProperty(srcProperty.Name)
If dstProperty IsNot Nothing Then
If dstProperty.PropertyType.IsAssignableFrom(srcProperty.PropertyType) = True Then
dstProperty.SetValue(dst, srcProperty.GetValue(src, Nothing), Nothing)
End If
End If
Next
End Sub
End Module
Module Module1
Class base_class
Dim _bval As Integer
Public Property bval() As Integer
Get
Return _bval
End Get
Set(ByVal value As Integer)
_bval = value
End Set
End Property
End Class
Class derived_class
Inherits base_class
Public _dval As Integer
Public Property dval() As Integer
Get
Return _dval
End Get
Set(ByVal value As Integer)
_dval = value
End Set
End Property
End Class
Sub Main()
' NARROWING CONVERSION TEST
Dim b As New base_class
b.bval = 10
Dim d As derived_class
'd = CType(b, derived_class) ' invalidcast exception
'd = DirectCast(b, derived_class) ' invalidcast exception
'd = TryCast(b, derived_class) ' returns 'nothing' for c
d = New derived_class
CopyProperties(d, b)
d.dval = 20
Console.WriteLine(b.bval)
Console.WriteLine(d.bval)
Console.WriteLine(d.dval)
Console.ReadLine()
End Sub
End Module
Tabii ki bu gerçekten oyuncu seçimi değil. Yeni bir türetilmiş nesne oluşturmak ve özellikleri ebeveynden kopyalamak, alt özellikleri boş bırakmaktır. Tek yapmam gereken buydu ve tek yapmanız gereken bu. Unutmayın ki sadece özellikleri kopyalar, sınıftaki üyeleri (genel değişkenler) değil (ancak bunu yapmak için, genel üyeleri ortaya çıkarmaktan utanç duyuyorsanız bunu genişletebilirsiniz).
Genel olarak çevrim aynı nesneye işaret eden 2 değişken oluşturur (burada mini eğitim, lütfen bana köşe durum istisnaları atmayın). Bunun önemli sonuçları var (okuyucu için egzersiz)!
Elbette, dilin neden temelden örnek türetmenize izin vermediğini, ancak tam tersini yaptığını söylemeliyim. Winforms metin kutusunun (türetilmiş) bir örneğini alabileceğiniz ve bunu Winforms denetimi türünün bir değişkeninde depolayabileceğiniz bir durum hayal edin. Elbette 'kontrol' nesneyi Tamam etrafında hareket ettirebilir ve metin kutusu ile ilgili tüm 'kontrol' şeylerini (örneğin, üst, sol, .text özellikleri) halledebilirsiniz. Metin kutusuna özgü şeyler (örn., .Multiline) bellekteki metin kutusuna işaret eden 'kontrol' türü değişkeni atmadan görülemez, ancak yine de bellekte oradadır.
Şimdi, bir kontrole sahip olduğunuzu ve metin kutusu türünde bir değişken için büyük harf kullanmak istediğinizi hayal edin. Bellekteki Kontrol, 'çok satırlı' ve diğer metin kutusu şeylerini kaçırıyor. Onlara başvurmaya çalışırsanız, kontrol sihirli bir şekilde çok satırlı bir mülk oluşturmaz! Özellik (buraya bir üye değişkeni gibi bakın, aslında bir değer depolayan - çünkü metin kutusu örneğinin belleğinde bir açık var ) olmalıdır . Döküm yaptığınız için, işaret ettiğiniz nesnenin aynı olması gerektiğini unutmayın. Dolayısıyla bu bir dil kısıtlaması değildir, böyle bir durumda olması felsefi olarak imkansızdır.
Bana gelince, tüm özellik alanlarını temel sınıftan ebeveyne şu şekilde kopyalamak yeterliydi:
using System.Reflection;
public static ChildClass Clone(BaseClass b)
{
ChildClass p = new ChildClass(...);
// Getting properties of base class
PropertyInfo[] properties = typeof(BaseClass).GetProperties();
// Copy all properties to parent class
foreach (PropertyInfo pi in properties)
{
if (pi.CanWrite)
pi.SetValue(p, pi.GetValue(b, null), null);
}
return p;
}
Herhangi bir nesne için evrensel bir çözüm burada bulunabilir
C # 7.0'dan itibaren, bunu yapmak için is anahtar sözcüğünü kullanabilirsiniz:
Tanımlanan sınıflarla:
class Base { /* Define base class */ }
class Derived : Base { /* Define derived class */ }
Daha sonra şöyle bir şey yapabilirsiniz:
void Funtion(Base b)
{
if (b is Derived d)
{
/* Do something with d which is now a variable of type Derived */
}
}
Hangisi şuna eşdeğerdir:
void Funtion(Base b)
{
Defined d;
if (b is Derived)
{
d = (Defined)b;
/* Do something with d */
}
}
Şimdi arayabilirsin:
Function(new Derived()); // Will execute code defined in if
Hem de
Function(new Base()); // Won't execute code defined in if
Bu şekilde, mahzunluğunuzun geçerli olacağından ve bir istisna atmayacağından emin olabilirsiniz!
Döküm için, gerçek bir nesne için eşit ya da Türü olmalıdır türetilmiş dan size döküm çalıştığınız Tip ...
veya tersini ifade etmek için, onu dönüştürmeye çalıştığınız Tür, nesnenin gerçek türü ile aynı veya temel bir sınıf olmalıdır.
gerçek nesneniz Baseclass türündeyse , onu türetilmiş bir türe dönüştüremezsiniz Type ...
ServiceStack kullananlar için serileştirme yaklaşımının bir varyasyonu:
var child = baseObject.ConvertTo<ChildType>();
veya daha ayrıntılı:
var child = baseObject.ToJson().FromJson<ChildType>();
ServiceStack'in serileştirmesi süper hızlı olabilir, ancak açıkça, bu düşük gecikmeli aktarımlarda büyük dönüşümler için veya oldukça karmaşık türler için bir çözüm değildir. Bu muhtemelen ServiceStack kullanan herkes için açıktır, ancak yorum beklentisiyle açıklığa kavuşturacağımı düşündüm.