Saya mencoba untuk mentransmisikan dari kelas induk ke kelas anak tetapi saya mendapatkan InvalidCastException. Kelas anak hanya memiliki satu properti tipe int. Adakah yang tahu apa yang harus saya lakukan?
Saya mencoba untuk mentransmisikan dari kelas induk ke kelas anak tetapi saya mendapatkan InvalidCastException. Kelas anak hanya memiliki satu properti tipe int. Adakah yang tahu apa yang harus saya lakukan?
Jawaban:
Cara sederhana untuk downcast di C # adalah untuk membuat serial orang tua dan kemudian deserialisasi ke dalam anak.
var serializedParent = JsonConvert.SerializeObject(parentInstance);
Child c = JsonConvert.DeserializeObject<Child>(serializedParent);
Saya memiliki aplikasi konsol sederhana yang mengubah hewan menjadi anjing, menggunakan dua baris kode di atas di sini
Anda tidak dapat mengubah mamalia menjadi seekor anjing - itu mungkin seekor kucing.
Anda tidak dapat memasukkan makanan ke dalam sandwich - itu mungkin burger keju.
Anda tidak dapat memasukkan mobil ke dalam Ferrari - itu mungkin Honda, atau lebih khusus lagi, Anda tidak dapat mentransmisikan Ferrari 360 Modena ke Ferrari 360 Challange Stradale - ada bagian yang berbeda, meskipun keduanya adalah Ferrari 360.
Instance yang dirujuk oleh referensi kelas dasar Anda bukanlah instance dari kelas anak Anda. Tidak ada yang salah.
Lebih spesifik:
Base derivedInstance = new Derived();
Base baseInstance = new Base();
Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException
Agar pemeran berhasil, instance yang Anda downcast haruslah instance dari kelas yang Anda downcasting (atau setidaknya, kelas yang Anda downcasting harus berada dalam hierarki kelas instance), jika tidak, cast akan gagal.
Saya telah melihat sebagian besar orang mengatakan bahwa casting eksplisit dari orang tua ke anak tidak mungkin, itu sebenarnya tidak benar. Mari kita mulai dengan revisi dan coba buktikan dengan contoh.
Seperti yang kita ketahui di .net semua casting memiliki dua kategori besar.
Jenis referensi memiliki tiga kasus situasional utama di mana setiap skenario dapat berbohong.
Kasus 1. Anak dari orang tua langsung atau tidak langsung
Employee e = new Employee();
Person p = (Person)e; //Allowed
Kasus 2. Variabel induk yang memegang objek induk (Tidak diperbolehkan)
Person p = new Person(); // p is true Person object
Employee e = (Employee)p; //Runtime err : InvalidCastException <-------- Yours issue
Kasus 3. Variabel induk memegang objek anak (Selalu Berhasil)
Catatan: Karena objek memiliki sifat polimorfik, dimungkinkan untuk variabel dari tipe kelas induk untuk menampung tipe anak.
Person p = new Employee(); // p actually is Employee
Employee e = (Employee)p; // Casting allowed
Kesimpulan: Setelah membaca di atas semua, semoga masuk akal sekarang seperti bagaimana konversi orang tua ke anak dimungkinkan (Kasus 3).
Jawab pertanyaan :
Jawaban Anda ada di kasus 2 Anda dapat melihat casting seperti itu tidak diizinkan oleh OOP dan Anda mencoba melanggar salah satu aturan dasar OOP. Jadi selalu pilih jalur yang aman.
Lebih jauh lagi, untuk menghindari situasi luar biasa seperti itu .net telah merekomendasikan penggunaan is / sebagai operator yang akan membantu Anda mengambil keputusan yang tepat dan menyediakan transmisi yang aman.
Ada beberapa kasus ketika pemeran seperti itu masuk akal.
Dalam kasus saya, saya menerima kelas BASE melalui jaringan, dan saya membutuhkan lebih banyak fitur untuk itu. Jadi memperolehnya untuk menanganinya di sisi saya dengan semua lonceng dan peluit yang saya inginkan, dan mentransmisikan kelas BASE yang diterima ke dalam kelas DERIVED bukanlah pilihan (Throws InvalidCastException of Course)
Salah satu solusi praktis yang dapat dipikirkan dengan baik adalah dengan mendeklarasikan kelas EXTENSION Helper yang sebenarnya TIDAK mewarisi kelas BASE, tetapi TERMASUK sebagai anggota.
public class BaseExtension
{
Base baseInstance;
public FakeDerived(Base b)
{
baseInstance = b;
}
//Helper methods and extensions to Base class added here
}
Jika Anda memiliki kopling longgar dan hanya membutuhkan beberapa fitur tambahan untuk kelas dasar tanpa BENAR - BENAR memiliki kebutuhan turunan yang mutlak, itu bisa menjadi solusi yang cepat dan sederhana.
BaseExtensionsini setidaknya menerapkan IBasesedemikian rupa sehingga Anda dapat menggunakannya dalam konteks yang serupa? Atau tidak penting untuk kebutuhan Anda?
Itu akan melanggar prinsip berorientasi objek. Saya akan mengatakan solusi elegan di sini dan di tempat lain dalam proyek ini menggunakan kerangka kerja pemetaan objek seperti AutoMapper untuk mengonfigurasi proyeksi.
Berikut adalah konfigurasi yang sedikit lebih kompleks daripada yang diperlukan tetapi cukup fleksibel untuk kebanyakan kasus:
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>();
});
}
}
Ketika aplikasi memulai panggilan AutoMapperConfiguration.Configure()dan kemudian Anda dapat memproyeksikan seperti ini:
ChildClassOne child = Mapper.Map<BaseClass, ChildClassOne>(baseClass);
Properti dipetakan menurut konvensi jadi jika kelas diwarisi, nama properti persis sama dan pemetaan dikonfigurasi secara otomatis. Anda dapat menambahkan properti tambahan dengan mengubah konfigurasi. Lihat dokumentasinya .
Paul, Anda tidak bertanya 'Bisakah saya melakukannya' - saya berasumsi Anda ingin tahu bagaimana melakukannya!
Kami harus melakukan ini pada sebuah proyek - ada banyak kelas yang kami siapkan secara umum hanya sekali, lalu menginisialisasi properti khusus untuk kelas turunan. Saya menggunakan VB jadi sampel saya dalam VB (noogies tangguh), tetapi saya mencuri sampel VB dari situs ini yang juga memiliki versi C # yang lebih baik:
Kode sampel:
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
Tentu saja ini bukan casting yang sebenarnya. Ini membuat objek turunan baru dan menyalin properti dari induk, membiarkan properti anak kosong. Hanya itu yang perlu saya lakukan dan sepertinya hanya itu yang perlu Anda lakukan. Perhatikan itu hanya menyalin properti, bukan anggota (variabel publik) di kelas (tetapi Anda dapat memperluasnya untuk melakukan itu jika Anda malu mengekspos anggota publik).
Casting secara umum membuat 2 variabel yang menunjuk ke objek yang sama (tutorial mini di sini, tolong jangan membuang pengecualian kasus sudut ke saya). Ada konsekuensi yang signifikan untuk ini (latihan untuk pembaca)!
Tentu saja saya harus mengatakan mengapa bahasa tidak membiarkan Anda pergi dari dasar ke contoh turunan, tetapi sebaliknya. bayangkan kasus di mana Anda dapat mengambil contoh dari kotak teks winforms (turunan) dan menyimpannya dalam variabel jenis kontrol Winforms. Tentu saja 'kontrol' dapat memindahkan objek sekitar OK dan Anda dapat menangani semua hal 'kontrol-y' tentang kotak teks (misalnya, atas, kiri, properti .text). Item khusus kotak teks (mis., .Multiline) tidak dapat dilihat tanpa mentransmisikan variabel jenis 'kontrol' yang menunjuk ke kotak teks di memori, tetapi masih ada di memori.
Sekarang bayangkan, Anda memiliki kontrol, dan Anda ingin memasukkan variabel jenis kotak teks ke dalamnya. Kontrol dalam memori kehilangan 'multiline' dan hal-hal textboxy lainnya. Jika Anda mencoba mereferensikannya, kontrol tidak akan secara ajaib menumbuhkan properti multiline! Properti (lihat seperti variabel anggota di sini, yang sebenarnya menyimpan nilai - karena ada di dalam memori instance kotak teks) harus ada. Karena Anda sedang melakukan casting, ingat, itu harus menjadi objek yang sama dengan yang Anda tunjuk. Oleh karena itu, ini bukanlah batasan bahasa, secara filosofis tidak mungkin terjadi dengan cara seperti itu.
Bagi saya, cukup menyalin semua bidang properti dari kelas dasar ke induk seperti ini:
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;
}
Solusi universal untuk objek apa pun dapat ditemukan di sini
Mulai C # 7.0, Anda dapat menggunakan kata kunci is untuk melakukan ini:
Dengan kelas tersebut ditentukan:
class Base { /* Define base class */ }
class Derived : Base { /* Define derived class */ }
Anda kemudian dapat melakukan sesuatu seperti:
void Funtion(Base b)
{
if (b is Derived d)
{
/* Do something with d which is now a variable of type Derived */
}
}
Yang mana setara dengan:
void Funtion(Base b)
{
Defined d;
if (b is Derived)
{
d = (Defined)b;
/* Do something with d */
}
}
Sekarang Anda dapat menghubungi:
Function(new Derived()); // Will execute code defined in if
Sebaik
Function(new Base()); // Won't execute code defined in if
Dengan begitu Anda bisa yakin bahwa downcast Anda akan valid dan tidak akan membuat pengecualian!
Untuk mentransmisikan, objek sebenarnya harus dari Jenis yang sama dengan atau berasal dari Jenis yang Anda coba untuk melemparkan ke ...
atau, untuk menyatakannya dengan cara yang berlawanan, Jenis yang Anda coba transmisikan harus sama dengan, atau kelas dasar, jenis objek yang sebenarnya.
jika objek Anda yang sebenarnya adalah tipe Baseclass , maka Anda tidak dapat mentransmisikannya ke kelas turunan Type ...
Variasi pada pendekatan serialisasi untuk mereka yang menggunakan ServiceStack:
var child = baseObject.ConvertTo<ChildType>();
atau lebih bertele-tele:
var child = baseObject.ToJson().FromJson<ChildType>();
Serialisasi ServiceStack mungkin super cepat, tetapi jelas, ini bukan solusi untuk konversi besar-besaran dalam transfer latensi rendah, atau untuk jenis yang sangat kompleks. Itu mungkin jelas bagi siapa pun yang menggunakan ServiceStack, tetapi saya pikir saya akan mengklarifikasi untuk mengantisipasi komentar.