Apakah mungkin untuk menetapkan objek kelas dasar ke referensi kelas turunan dengan typecast eksplisit di C # ?.
Saya telah mencobanya dan itu menciptakan kesalahan waktu berjalan.
c#
casting
derived-class
base-class
downcast
Maddy.Shik
sumber
sumber
Derived
, tetapi Anda dapat memperlakukanDerived
referensi sebagaiBase
referensi.Base
, dan yang lain membuat instanceDerived
. Jika Anda memanggil metode virtualb
yang telah digantiDerived
, Anda akan melihatDerived
perilakunya jika Anda memiliki instanceDerived
. Tetapi tidak tepat untuk membahas detail di utas komentar Stack Overflow - Anda harus benar-benar membaca buku C # atau tutorial yang bagus, karena ini adalah hal yang cukup mendasar.Tidak, itu tidak mungkin karena menugaskannya ke referensi kelas turunan akan seperti mengatakan "Kelas dasar adalah pengganti yang mampu sepenuhnya untuk kelas turunan, ia dapat melakukan semua yang dapat dilakukan kelas turunan", yang tidak benar karena kelas turunan dalam penawaran umum lebih banyak fungsionalitas daripada kelas dasarnya (setidaknya, itulah ide di balik warisan).
Anda bisa menulis konstruktor di kelas turunan mengambil objek kelas dasar sebagai parameter, menyalin nilai.
Sesuatu seperti ini:
public class Base { public int Data; public void DoStuff() { // Do stuff with data } } public class Derived : Base { public int OtherData; public Derived(Base b) { this.Data = b.Data; OtherData = 0; // default value } public void DoOtherStuff() { // Do some other stuff } }
Dalam hal ini Anda akan menyalin objek dasar dan mendapatkan objek kelas turunan yang berfungsi penuh dengan nilai default untuk anggota turunan. Dengan cara ini Anda juga dapat menghindari masalah yang ditunjukkan oleh Jon Skeet:
Base b = new Base();//base class Derived d = new Derived();//derived class b.DoStuff(); // OK d.DoStuff(); // Also OK b.DoOtherStuff(); // Won't work! d.DoOtherStuff(); // OK d = new Derived(b); // Copy construct a Derived with values of b d.DoOtherStuff(); // Now works!
sumber
Saya punya masalah ini dan menyelesaikannya dengan menambahkan metode yang mengambil parameter tipe dan mengubah objek saat ini menjadi tipe itu.
public TA As<TA>() where TA : Base { var type = typeof (TA); var instance = Activator.CreateInstance(type); PropertyInfo[] properties = type.GetProperties(); foreach (var property in properties) { property.SetValue(instance, property.GetValue(this, null), null); } return (TA)instance; }
Itu berarti Anda dapat menggunakannya dalam kode Anda seperti ini:
var base = new Base(); base.Data = 1; var derived = base.As<Derived>(); Console.Write(derived.Data); // Would output 1
sumber
Seperti yang telah dijawab banyak orang, Tidak.
Saya menggunakan kode berikut pada kesempatan yang tidak menguntungkan ketika saya perlu menggunakan tipe dasar sebagai tipe turunan. Ya, itu adalah pelanggaran Prinsip Substitusi Liskov (LSP) dan ya, sebagian besar waktu kami lebih menyukai komposisi daripada warisan. Alat peraga untuk Markus Knappen Johansson yang jawaban aslinya didasarkan pada ini.
Kode ini di kelas dasar:
public T As<T>() { var type = typeof(T); var instance = Activator.CreateInstance(type); if (type.BaseType != null) { var properties = type.BaseType.GetProperties(); foreach (var property in properties) if (property.CanWrite) property.SetValue(instance, property.GetValue(this, null), null); } return (T) instance; }
Memungkinkan:
Karena menggunakan refleksi, maka "mahal". Gunakan sesuai.
sumber
user-defined conversions to or from a base class are not allowed
Saya melihat alasan untuk ini, tetapi saya kecewa, karena akan sangat menyenangkan jika memungkinkan ini ..if (type.BaseType != null)
Pernyataan relatif terhadap A. Markus Knappen Johansson Mengapa demikian? Itu berarti itu akan memungkinkan Jenis Panggilan yang tidak Berasal dari MyBaseClass (atau apa pun dalam hal ini). Saya menyadari itu masih akan menyebabkan kesalahan kompilator jika Ditugaskan ke myDerivedObject, tetapi jika itu hanya digunakan sebagai Ekspresi, itu akan mengkompilasi dan pada saat run-time hanya membuat myDerivedObject tanpa data disalin dari "myBaseObject". Saya tidak bisa membayangkan kasus penggunaan untuk itu.Tidak itu tidak mungkin, karena itu kesalahan runtime Anda.
Tapi Anda bisa menetapkan sebuah instance dari kelas turunan ke variabel tipe kelas dasar.
sumber
Solusi dengan JsonConvert (bukan typecast)
Hari ini saya menghadapi masalah yang sama dan saya menemukan solusi sederhana dan cepat untuk masalah penggunaan
JsonConvert
.var base = new BaseClass(); var json = JsonConvert.SerializeObject(base); DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
sumber
Seperti yang dikatakan semua orang di sini, itu tidak mungkin secara langsung.
Metode yang saya suka dan agak bersih, adalah menggunakan Pemeta Objek seperti AutoMapper .
Ini akan melakukan tugas menyalin properti dari satu contoh ke contoh lainnya (Tidak harus tipe yang sama) secara otomatis.
sumber
Memperluas jawaban @ ybo - itu tidak mungkin karena instance yang Anda miliki dari kelas dasar sebenarnya bukan turunan dari kelas turunan. Ia hanya tahu tentang anggota kelas dasar, dan tidak tahu apa-apa tentang kelas turunan.
Alasan Anda dapat mentransmisikan instance dari kelas turunan ke instance kelas dasar adalah karena kelas turunan sebenarnya sudah merupakan turunan dari kelas dasar, karena sudah memiliki anggota tersebut. Kebalikannya tidak bisa dikatakan.
sumber
Anda dapat mentransmisikan variabel yang diketik sebagai kelas dasar ke jenis kelas turunan; namun, dengan kebutuhan ini akan melakukan pemeriksaan runtime, untuk melihat apakah objek aktual yang terlibat memiliki tipe yang benar.
Setelah dibuat, jenis objek tidak dapat diubah (paling tidak, mungkin ukurannya tidak sama). Namun, Anda dapat mengonversi sebuah instance, membuat instance baru dari tipe kedua - tetapi Anda perlu menulis kode konversi secara manual.
sumber
Tidak, tidak mungkin.
Pertimbangkan skenario di mana ACBus adalah kelas turunan Bus kelas dasar. ACBus memiliki fitur seperti TurnOnAC dan TurnOffAC yang beroperasi pada bidang bernama ACState. TurnOnAC menyetel ACState ke aktif dan TurnOffAC menyetel ACState ke nonaktif. Jika Anda mencoba menggunakan fitur TurnOnAC dan TurnOffAC di Bus, itu tidak masuk akal.
sumber
class Program { static void Main(string[] args) { a a1 = new b(); a1.print(); } } class a { public a() { Console.WriteLine("base class object initiated"); } public void print() { Console.WriteLine("base"); } } class b:a { public b() { Console.WriteLine("child class object"); } public void print1() { Console.WriteLine("derived"); } }
}
ketika kita membuat objek kelas anak, objek kelas dasar dimulai secara otomatis sehingga variabel referensi kelas dasar dapat menunjuk ke objek kelas anak.
tetapi tidak sebaliknya karena variabel referensi kelas anak tidak dapat menunjuk ke objek kelas dasar karena tidak ada objek kelas anak yang dibuat.
dan juga perhatikan bahwa variabel referensi kelas dasar hanya dapat memanggil anggota kelas dasar.
sumber
Sebenarnya ADA cara untuk melakukan ini. Pikirkan tentang bagaimana Anda dapat menggunakan Newtonsoft JSON untuk deserialisasi objek dari json. Ini akan (atau setidaknya dapat) mengabaikan elemen yang hilang dan mengisi semua elemen yang diketahui.
Jadi, inilah cara saya melakukannya. Contoh kode kecil akan mengikuti penjelasan saya.
Buat instance objek Anda dari kelas dasar dan isilah sesuai dengan itu.
Menggunakan kelas "jsonconvert" dari Newtonsoft json, membuat serial objek itu menjadi string json.
Sekarang buat objek sub kelas Anda dengan deserialisasi dengan string json yang dibuat pada langkah 2. Ini akan membuat turunan dari sub kelas Anda dengan semua properti dari kelas dasar.
Ini bekerja seperti pesona! Jadi .. kapan ini berguna? Beberapa orang bertanya kapan ini masuk akal dan menyarankan untuk mengubah skema OP untuk mengakomodasi fakta bahwa Anda tidak dapat melakukan ini secara native dengan warisan kelas (dalam .Net).
Dalam kasus saya, saya memiliki kelas pengaturan yang berisi semua pengaturan "dasar" untuk layanan. Layanan tertentu memiliki lebih banyak opsi dan berasal dari tabel DB yang berbeda, sehingga kelas tersebut mewarisi kelas dasar. Mereka semua memiliki serangkaian opsi yang berbeda. Jadi, saat mengambil data untuk layanan, akan jauh lebih mudah untuk mengisi FIRST nilai menggunakan instance objek dasar. Salah satu metode untuk melakukan ini dengan kueri DB tunggal. Tepat setelah itu, saya membuat objek subkelas menggunakan metode yang diuraikan di atas. Saya kemudian membuat kueri kedua dan mengisi semua nilai dinamis pada objek subkelas.
Output akhir adalah kelas turunan dengan semua opsi yang ditetapkan. Mengulangi ini untuk sub kelas baru tambahan hanya membutuhkan beberapa baris kode. Ini sederhana, dan menggunakan paket yang telah dicoba dan diuji (Newtonsoft) untuk membuat keajaiban bekerja.
Kode contoh ini adalah vb.Net, tetapi Anda dapat dengan mudah mengubahnya ke c #.
' First, create the base settings object. Dim basePMSettngs As gtmaPayMethodSettings = gtmaPayments.getBasePayMethodSetting(payTypeId, account_id) Dim basePMSettingsJson As String = JsonConvert.SerializeObject(basePMSettngs, Formatting.Indented) ' Create a pmSettings object of this specific type of payment and inherit from the base class object Dim pmSettings As gtmaPayMethodAimACHSettings = JsonConvert.DeserializeObject(Of gtmaPayMethodAimACHSettings)(basePMSettingsJson)
sumber
var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));
. Saya hanya akan menggunakan ini untuk pengujian unit dan "peretasan" non-produksi lainnya!Anda dapat menggunakan Extention:
public static void CopyOnlyEqualProperties<T>(this T objDest, object objSource) where T : class { foreach (PropertyInfo propInfo in typeof(T).GetProperties()) if (objSource.GetType().GetProperties().Any(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType())) propInfo.SetValue(objDest, objSource.GetType().GetProperties().First(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()).GetValue(objSource)); }
Dalam kode:
public class BaseClass { public string test{ get; set;} } public Derived : BaseClass { //Some properies } public void CopyProps() { BaseClass baseCl =new BaseClass(); baseCl.test="Hello"; Derived drv=new Derived(); drv.CopyOnlyEqualProperties(baseCl); //Should return Hello to the console now in derived class. Console.WriteLine(drv.test); }
sumber
Mungkin tidak relevan, tetapi saya dapat menjalankan kode pada objek turunan berdasarkan basisnya. Ini jelas lebih hacky daripada yang saya inginkan, tetapi berhasil:
public static T Cast<T>(object obj) { return (T)obj; }
...
//Invoke parent object's json function MethodInfo castMethod = this.GetType().GetMethod("Cast").MakeGenericMethod(baseObj.GetType()); object castedObject = castMethod.Invoke(null, new object[] { baseObj }); MethodInfo jsonMethod = baseObj.GetType ().GetMethod ("ToJSON"); return (string)jsonMethod.Invoke (castedObject,null);
sumber
Anda dapat melakukan ini dengan menggunakan generik.
public class BaseClass { public int A { get; set; } public int B { get; set; } private T ConvertTo<T>() where T : BaseClass, new() { return new T { A = A, B = B } } public DerivedClass1 ConvertToDerivedClass1() { return ConvertTo<DerivedClass1>(); } public DerivedClass2 ConvertToDerivedClass2() { return ConvertTo<DerivedClass2>(); } } public class DerivedClass1 : BaseClass { public int C { get; set; } } public class DerivedClass2 : BaseClass { public int D { get; set; } }
Anda mendapatkan tiga manfaat menggunakan pendekatan ini.
sumber
Saya tahu ini sudah tua tetapi saya telah menggunakan ini dengan sukses cukup lama.
private void PopulateDerivedFromBase<TB,TD>(TB baseclass,TD derivedclass) { //get our baseclass properties var bprops = baseclass.GetType().GetProperties(); foreach (var bprop in bprops) { //get the corresponding property in the derived class var dprop = derivedclass.GetType().GetProperty(bprop.Name); //if the derived property exists and it's writable, set the value if (dprop != null && dprop.CanWrite) dprop.SetValue(derivedclass,bprop.GetValue(baseclass, null),null); } }
sumber
Saya menggabungkan beberapa bagian dari jawaban sebelumnya (terima kasih kepada para penulis tersebut) dan menyusun kelas statis sederhana dengan dua metode yang kami gunakan.
Ya, itu sederhana, tidak itu tidak mencakup semua skenario, ya itu bisa diperluas dan dibuat lebih baik, tidak itu tidak sempurna, ya itu mungkin bisa dibuat lebih efisien, bukan itu bukan yang terbaik sejak roti iris, ya ada full-on pemeta objek paket nuget yang kuat di luar sana yang jauh lebih baik untuk penggunaan berat, dll, yada yada - tetapi ini berfungsi untuk kebutuhan dasar kita :)
Dan tentu saja ia akan mencoba memetakan nilai dari objek apa pun ke objek apa pun, diturunkan atau tidak (hanya properti publik yang diberi nama sama saja - mengabaikan yang lainnya).
PEMAKAIAN:
SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 }; // creates new object of type "RealPerson" and assigns any matching property // values from the puppet object // (this method requires that "RealPerson" have a parameterless constructor ) RealPerson person = ObjectMapper.MapToNewObject<RealPerson>(puppet); // OR // create the person object on our own // (so RealPerson can have any constructor type that it wants) SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 }; RealPerson person = new RealPerson("tall") {Name = "Steve"}; // maps and overwrites any matching property values from // the puppet object to the person object so now our person's age will get set to 5 and // the name "Steve" will get overwritten with "Elmo" in this example ObjectMapper.MapToExistingObject(puppet, person);
KELAS UTILITAS STATIK:
public static class ObjectMapper { // the target object is created on the fly and the target type // must have a parameterless constructor (either compiler-generated or explicit) public static Ttarget MapToNewObject<Ttarget>(object sourceobject) where Ttarget : new() { // create an instance of the target class Ttarget targetobject = (Ttarget)Activator.CreateInstance(typeof(Ttarget)); // map the source properties to the target object MapToExistingObject(sourceobject, targetobject); return targetobject; } // the target object is created beforehand and passed in public static void MapToExistingObject(object sourceobject, object targetobject) { // get the list of properties available in source class var sourceproperties = sourceobject.GetType().GetProperties().ToList(); // loop through source object properties sourceproperties.ForEach(sourceproperty => { var targetProp = targetobject.GetType().GetProperty(sourceproperty.Name); // check whether that property is present in target class and is writeable if (targetProp != null && targetProp.CanWrite) { // if present get the value and map it var value = sourceobject.GetType().GetProperty(sourceproperty.Name).GetValue(sourceobject, null); targetobject.GetType().GetProperty(sourceproperty.Name).SetValue(targetobject, value, null); } }); } }
sumber
Anda dapat menggunakan konstruktor salinan yang segera memanggil konstruktor instans, atau jika konstruktor instans Anda melakukan lebih dari tugas, maka konstruktor salinan menetapkan nilai yang masuk ke instans.
class Person { // Copy constructor public Person(Person previousPerson) { Name = previousPerson.Name; Age = previousPerson.Age; } // Copy constructor calls the instance constructor. public Person(Person previousPerson) : this(previousPerson.Name, previousPerson.Age) { } // Instance constructor. public Person(string name, int age) { Name = name; Age = age; } public int Age { get; set; } public string Name { get; set; } }
Merujuk Dokumentasi Microsoft C # di bawah Pembuat untuk contoh ini yang pernah mengalami masalah ini di masa lalu.
sumber
Solusi lain adalah menambahkan metode ekstensi seperti ini:
public static void CopyProperties(this object destinationObject, object sourceObject, bool overwriteAll = true) { try { if (sourceObject != null) { PropertyInfo[] sourceProps = sourceObject.GetType().GetProperties(); List<string> sourcePropNames = sourceProps.Select(p => p.Name).ToList(); foreach (PropertyInfo pi in destinationObject.GetType().GetProperties()) { if (sourcePropNames.Contains(pi.Name)) { PropertyInfo sourceProp = sourceProps.First(srcProp => srcProp.Name == pi.Name); if (sourceProp.PropertyType == pi.PropertyType) if (overwriteAll || pi.GetValue(destinationObject, null) == null) { pi.SetValue(destinationObject, sourceProp.GetValue(sourceObject, null), null); } } } } } catch (ApplicationException ex) { throw; } }
kemudian miliki konstruktor di setiap kelas turunan yang menerima kelas dasar:
public class DerivedClass: BaseClass { public DerivedClass(BaseClass baseModel) { this.CopyProperties(baseModel); } }
Ini juga akan secara opsional menimpa properti tujuan jika sudah disetel (bukan null) atau belum.
sumber
Tidak hanya eksplisit, tetapi juga konversi implisit dimungkinkan.
Bahasa C # tidak mengizinkan operator konversi seperti itu, tetapi Anda masih dapat menulisnya menggunakan C # murni dan mereka berfungsi. Perhatikan bahwa kelas yang mendefinisikan operator konversi implisit (
Derived
) dan kelas yang menggunakan operator (Program
) harus didefinisikan dalam rakitan terpisah (misalnyaDerived
kelas dalam alibrary.dll
yang direferensikan denganprogram.exe
memuatProgram
kelas).//In library.dll: public class Base { } public class Derived { [System.Runtime.CompilerServices.SpecialName] public static Derived op_Implicit(Base a) { return new Derived(a); //Write some Base -> Derived conversion code here } [System.Runtime.CompilerServices.SpecialName] public static Derived op_Explicit(Base a) { return new Derived(a); //Write some Base -> Derived conversion code here } } //In program.exe: class Program { static void Main(string[] args) { Derived z = new Base(); //Visual Studio can show squiggles here, but it compiles just fine. } }
Ketika Anda mereferensikan pustaka menggunakan Referensi Proyek di Visual Studio, VS memperlihatkan coretan ketika Anda menggunakan konversi implisit, tapi itu mengkompilasi dengan baik. Jika Anda hanya mereferensikan
library.dll
, tidak ada coretan.sumber
System.Runtime.CompilerServices.SpecialName
Atribut? Dokumen untuk setiap versi dari yang paling awal tersedia (2.0) hingga "versi saat ini" (4.6? "Siapa? Siapa?") Tidak mengatakan apa fungsinya, tetapi katakanlah "Kelas SpecialNameAttribute saat ini tidak digunakan dalam .NET Kerangka kerja, tetapi dicadangkan untuk penggunaan di masa mendatang. ". Lihat: [link] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx ).where T : Delegate
properti parametrized alias pengindeks dll dll).what does System.Runtime.CompilerServices.SpecialName Attribute do?
- Ini digunakan untuk menandai metode yang dihasilkan oleh beberapa konstruk kenyamanan khusus dari bahasa .Net tingkat tinggi: pengakses properti, pengakses acara, konstruktor, operator, pengindeks, dll. Kecuali jika metode IL ditandai denganspecialname
itu tidak akan terlihat sebagai properti / acara / konstruktor dan itu hanya akan dikenali sebagai metode normal. Menandai secara manual metode yang dinamai dengan tepat dengan atribut ini hanya secara manual melakukan sedikit tugas kompiler.op_Exponent
metode dan tandai denganspecialname
atribut.Bagaimana tentang:
public static T As<T>(this object obj) { return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj)); }
sumber
Cara terbaik untuk menambahkan semua properti dasar ke item turunan adalah menggunakan refleksi di costructor. Coba kode ini, tanpa membuat metode atau contoh.
public Derived(Base item) :base() { Type type = item.GetType(); System.Reflection.PropertyInfo[] properties = type.GetProperties(); foreach (var property in properties) { try { property.SetValue(this, property.GetValue(item, null), null); } catch (Exception) { } } }
sumber
Saya tidak setuju bahwa itu tidak mungkin. Anda bisa melakukannya seperti ini:
public class Auto { public string Make {get; set;} public string Model {get; set;} } public class Sedan : Auto { public int NumberOfDoors {get; set;} } public static T ConvertAuto<T>(Sedan sedan) where T : class { object auto = sedan; return (T)loc; }
Pemakaian:
var sedan = new Sedan(); sedan.NumberOfDoors = 4; var auto = ConvertAuto<Auto>(sedan);
sumber
var auto =
masih tipesedan
Beginilah cara saya memecahkan masalah ini untuk ladang. Anda dapat melakukan iterasi yang sama melalui properti jika Anda mau. Anda mungkin ingin melakukan beberapa pemeriksaan untuk
null
dll, tetapi inilah idenya.public static DerivedClass ConvertFromBaseToDerived<BaseClass, DerivedClass>(BaseClass baseClass) where BaseClass : class, new() where DerivedClass : class, BaseClass, new() { DerivedClass derived = (DerivedClass)Activator.CreateInstance(typeof(DerivedClass)); derived.GetType().GetFields().ToList().ForEach(field => { var base_ = baseClass.GetType().GetField(field.Name).GetValue(baseClass); field.SetValue(derived, base_); }); return derived; }
sumber
Anda cukup membuat serial objek dasar ke JSON dan kemudian deserialisasi ke objek turunan.
sumber
Bukan dalam Pengertian Tradisional ... Ubah ke Json, lalu ke objek Anda, dan boom, selesai! Jesse di atas memiliki jawaban yang diposting terlebih dahulu, tetapi tidak menggunakan metode ekstensi ini yang membuat prosesnya jauh lebih mudah. Buat beberapa metode ekstensi:
public static string ConvertToJson<T>(this T obj) { return JsonConvert.SerializeObject(obj); } public static T ConvertToObject<T>(this string json) { if (string.IsNullOrEmpty(json)) { return Activator.CreateInstance<T>(); } return JsonConvert.DeserializeObject<T>(json); }
Tempatkan mereka di kotak peralatan Anda selamanya, maka Anda selalu dapat melakukan ini:
var derivedClass = baseClass.ConvertToJson().ConvertToObject<derivedClass>();
Ah, kekuatan JSON.
Ada beberapa masalah dengan pendekatan ini: Kami benar-benar membuat objek baru, bukan mentransmisi, yang mungkin menjadi masalah atau tidak. Bidang pribadi tidak akan ditransfer, konstruktor dengan parameter tidak akan dipanggil, dll. Ada kemungkinan bahwa beberapa json anak tidak akan ditetapkan. Aliran tidak ditangani secara bawaan oleh JsonConvert. Namun, jika kelas kita tidak bergantung pada bidang privat dan konstruktor, ini adalah metode yang sangat efektif untuk memindahkan data dari kelas ke kelas tanpa pemetaan dan memanggil konstruktor, yang merupakan alasan utama mengapa kita ingin melakukan cast di tempat pertama.
sumber
Tidak, lihat pertanyaan yang saya ajukan ini - Menyiarkan di .NET menggunakan obat generik
Cara terbaik adalah dengan membuat konstruktor default pada kelas, membuat lalu memanggil
Initialise
metodesumber