เป็นไปได้หรือไม่ที่จะกำหนดอ็อบเจ็กต์คลาสพื้นฐานให้กับการอ้างอิงคลาสที่ได้รับด้วยตัวพิมพ์ที่ชัดเจนใน C #?
ฉันได้ลองแล้วและเกิดข้อผิดพลาดรันไทม์
เป็นไปได้หรือไม่ที่จะกำหนดอ็อบเจ็กต์คลาสพื้นฐานให้กับการอ้างอิงคลาสที่ได้รับด้วยตัวพิมพ์ที่ชัดเจนใน C #?
ฉันได้ลองแล้วและเกิดข้อผิดพลาดรันไทม์
คำตอบ:
ไม่ได้การอ้างอิงไปยังคลาสที่ได้รับจริงต้องอ้างถึงอินสแตนซ์ของคลาสที่ได้รับ (หรือ null) มิฉะนั้นคุณจะคาดหวังให้มันทำงานอย่างไร?
ตัวอย่างเช่น:
object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?
หากคุณต้องการแปลงอินสแตนซ์ของประเภทพื้นฐานเป็นประเภทที่ได้รับฉันขอแนะนำให้คุณเขียนวิธีการเพื่อสร้างอินสแตนซ์ประเภทที่ได้รับที่เหมาะสม หรือดูต้นไม้มรดกของคุณอีกครั้งแล้วลองออกแบบใหม่เพื่อที่คุณจะได้ไม่ต้องทำสิ่งนี้ตั้งแต่แรก
Derived
เดียว แต่คุณสามารถใช้การDerived
อ้างอิงเป็นBase
ข้อมูลอ้างอิงได้
Base
และอีกอันสร้างอินสแตนซ์ของDerived
. ถ้าคุณโทรวิธีเสมือนบนb
ซึ่งถูกแทนที่ในDerived
คุณจะเห็นพฤติกรรมถ้าคุณได้มีตัวอย่างของDerived
Derived
แต่ไม่เหมาะสมจริงๆที่จะเข้าไปดูรายละเอียดในเธรดความคิดเห็น Stack Overflow - คุณควรอ่านหนังสือ C # หรือแบบฝึกหัดที่ดีเนื่องจากเป็นสิ่งพื้นฐานที่ค่อนข้างดี
ไม่เป็นไปไม่ได้เนื่องจากการกำหนดให้กับการอ้างอิงคลาสที่ได้รับมาจะเหมือนกับการพูดว่า "คลาสพื้นฐานเป็นการแทนที่คลาสที่ได้รับอย่างสมบูรณ์ซึ่งสามารถทำทุกอย่างที่คลาสที่ได้รับสามารถทำได้" ซึ่งไม่เป็นความจริงเนื่องจากคลาสที่ได้รับในข้อเสนอทั่วไป ฟังก์ชันมากกว่าคลาสพื้นฐาน (อย่างน้อยนั่นคือแนวคิดเบื้องหลังการสืบทอด)
คุณสามารถเขียนตัวสร้างในคลาสที่ได้รับโดยใช้อ็อบเจ็กต์คลาสฐานเป็นพารามิเตอร์คัดลอกค่า
สิ่งนี้:
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
}
}
ในกรณีนั้นคุณจะคัดลอกอ็อบเจ็กต์พื้นฐานและรับคลาสอ็อบเจ็กต์ที่ได้รับการทำงานอย่างสมบูรณ์พร้อมค่าดีฟอลต์สำหรับสมาชิกที่ได้รับ ด้วยวิธีนี้คุณสามารถหลีกเลี่ยงปัญหาที่ 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!
ฉันมีปัญหานี้และแก้ไขได้โดยการเพิ่มวิธีการที่รับพารามิเตอร์ประเภทและแปลงวัตถุปัจจุบันเป็นประเภทนั้น
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;
}
นั่นหมายความว่าคุณสามารถใช้ในโค้ดของคุณได้ดังนี้:
var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1
อย่างที่คนอื่น ๆ ตอบไม่ได้
ฉันใช้รหัสต่อไปนี้ในโอกาสที่โชคร้ายเมื่อฉันจำเป็นต้องใช้ประเภทพื้นฐานเป็นประเภทที่ได้รับ ใช่มันเป็นการละเมิดหลักการ Liskov Substitution Principle (LSP) และใช่ส่วนใหญ่แล้วเราชอบการแต่งเพลงมากกว่าการถ่ายทอดทางพันธุกรรม อุปกรณ์ประกอบฉากสำหรับ Markus Knappen Johansson ซึ่งมีคำตอบเดิมตามนี้
รหัสนี้ในคลาสพื้นฐาน:
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;
}
อนุญาต:
derivedObject = baseObect.As<derivedType>()
เนื่องจากใช้การสะท้อนแสงจึงมีราคา "แพง" ใช้ตามนั้น
user-defined conversions to or from a base class are not allowed
ฉันเห็นสาเหตุของสิ่งนี้ แต่ผิดหวัง มันจะสนุกมากถ้ามันยอมให้ ..
if (type.BaseType != null)
คำชี้แจงที่เกี่ยวข้องกับ A. ของ Markus Knappen Johansson ทำไมถึงเป็นอย่างนั้น? นั่นหมายความว่าจะอนุญาตประเภทในการโทรที่ไม่ได้มาจาก MyBaseClass (หรืออะไรก็ได้สำหรับเรื่องนั้น) ฉันรู้ว่ามันจะยังคงทำให้เกิดข้อผิดพลาดของคอมไพเลอร์หากมอบหมายให้ myDerivedObject แต่ถ้าใช้เป็นเพียงนิพจน์มันจะคอมไพล์และในขณะรันไทม์เพียงแค่สร้าง myDerivedObject โดยไม่มีข้อมูลใด ๆ ที่คัดลอกมาจาก "myBaseObject" ฉันนึกไม่ถึงกรณีการใช้งานสำหรับสิ่งนั้น
ไม่เป็นไปไม่ได้ด้วยเหตุนี้ข้อผิดพลาดรันไทม์ของคุณ
แต่คุณสามารถกำหนดอินสแตนซ์ของคลาสที่ได้รับให้กับตัวแปรของประเภทคลาสพื้นฐาน
วันนี้ฉันประสบปัญหาเดียวกันและพบวิธีแก้ปัญหาที่ง่ายและรวดเร็วโดยใช้JsonConvert
ไฟล์.
var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
อย่างที่ทุกคนพูดกันตรงนี้ไม่ได้เลย
วิธีที่ฉันชอบและค่อนข้างสะอาดคือใช้ Object Mapper เช่นAutoMapper AutoMapper
มันจะทำหน้าที่คัดลอกคุณสมบัติจากอินสแตนซ์หนึ่งไปยังอีกอินสแตนซ์หนึ่ง (ไม่จำเป็นต้องเป็นประเภทเดียวกัน) โดยอัตโนมัติ
การขยายคำตอบของ @ ybo - เป็นไปไม่ได้เนื่องจากอินสแตนซ์ที่คุณมีของคลาสพื้นฐานไม่ใช่อินสแตนซ์ของคลาสที่ได้รับมา มันรู้เกี่ยวกับสมาชิกของคลาสพื้นฐานเท่านั้นและไม่รู้อะไรเกี่ยวกับคลาสที่ได้รับมา
เหตุผลที่คุณสามารถสร้างอินสแตนซ์ของคลาสที่ได้รับไปยังอินสแตนซ์ของคลาสพื้นฐานได้เนื่องจากคลาสที่ได้รับนั้นเป็นอินสแตนซ์ของคลาสพื้นฐานอยู่แล้วเนื่องจากมีสมาชิกเหล่านั้นอยู่แล้ว สิ่งที่ตรงกันข้ามไม่สามารถพูดได้
คุณสามารถแคสต์ตัวแปรที่พิมพ์เป็นคลาสพื้นฐานไปยังประเภทของคลาสที่ได้รับ อย่างไรก็ตามตามความจำเป็นสิ่งนี้จะทำการตรวจสอบรันไทม์เพื่อดูว่าวัตถุจริงที่เกี่ยวข้องเป็นประเภทที่ถูกต้องหรือไม่
เมื่อสร้างแล้วจะไม่สามารถเปลี่ยนประเภทของวัตถุได้ (อย่างน้อยก็อาจมีขนาดไม่เท่ากัน) อย่างไรก็ตามคุณสามารถแปลงอินสแตนซ์สร้างอินสแตนซ์ใหม่ของประเภทที่สองได้ แต่คุณต้องเขียนโค้ด Conversion ด้วยตนเอง
ไม่เป็นไปไม่ได้
พิจารณาสถานการณ์ที่ ACBus เป็นคลาสที่ได้รับมาของบัสคลาสพื้นฐาน ACBus มีคุณสมบัติเช่น TurnOnAC และ TurnOffAC ซึ่งทำงานบนฟิลด์ชื่อ ACState TurnOnAC ตั้งค่า ACState เป็นเปิดและ TurnOffAC ตั้งค่า ACState เป็นปิด หากคุณพยายามใช้คุณสมบัติ TurnOnAC และ TurnOffAC บน Bus ก็ไม่สมเหตุสมผล
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");
}
}
}
เมื่อเราสร้างอ็อบเจ็กต์คลาสลูกอ็อบเจ็กต์คลาสพื้นฐานจะเริ่มต้นโดยอัตโนมัติดังนั้นตัวแปรอ้างอิงคลาสพื้นฐานสามารถชี้ไปที่อ็อบเจ็กต์คลาสลูก
แต่ไม่ใช่ในทางกลับกันเนื่องจากตัวแปรอ้างอิงคลาสย่อยไม่สามารถชี้ไปที่อ็อบเจ็กต์คลาสพื้นฐานได้เนื่องจากไม่มีการสร้างอ็อบเจ็กต์คลาสลูก
และสังเกตด้วยว่าตัวแปรอ้างอิงคลาสพื้นฐานสามารถเรียกเฉพาะสมาชิกคลาสพื้นฐานเท่านั้น
มีวิธีการทำเช่นนี้จริงๆ ลองนึกดูว่าคุณจะใช้ Newtonsoft JSON เพื่อแยกวัตถุออกจาก json ได้อย่างไร มันจะ (หรืออย่างน้อยก็สามารถ) ละเว้นองค์ประกอบที่ขาดหายไปและเติมองค์ประกอบทั้งหมดที่มันรู้
นี่คือวิธีที่ฉันทำ ตัวอย่างโค้ดขนาดเล็กจะเป็นไปตามคำอธิบายของฉัน
สร้างอินสแตนซ์ของออบเจ็กต์ของคุณจากคลาสพื้นฐานและเติมข้อมูลตามนั้น
ใช้คลาส "jsonconvert" ของ Newtonsoft json ทำให้วัตถุนั้นเป็นอนุกรมเป็นสตริง json
ตอนนี้สร้างอ็อบเจ็กต์คลาสย่อยของคุณโดย deserializing ด้วยสตริง json ที่สร้างในขั้นตอนที่ 2 สิ่งนี้จะสร้างอินสแตนซ์ของคลาสย่อยของคุณพร้อมคุณสมบัติทั้งหมดของคลาสพื้นฐาน
งานนี้เหมือนมีเสน่ห์! แล้ว.. เมื่อไหร่จะมีประโยชน์? บางคนถามว่าเมื่อไหร่จะสมเหตุสมผลและแนะนำให้เปลี่ยนสคีมาของ OP เพื่อรองรับความจริงที่ว่าคุณไม่สามารถทำสิ่งนี้กับการสืบทอดคลาสได้ (ใน. Net)
ในกรณีของฉันฉันมีคลาสการตั้งค่าที่มีการตั้งค่า "ฐาน" ทั้งหมดสำหรับบริการ บริการเฉพาะมีตัวเลือกมากกว่าและมาจากตาราง DB อื่นดังนั้นคลาสเหล่านั้นจะสืบทอดคลาสพื้นฐาน พวกเขาทั้งหมดมีชุดตัวเลือกที่แตกต่างกัน ดังนั้นเมื่อดึงข้อมูลสำหรับบริการ FIRST จะเติมค่าโดยใช้อินสแตนซ์ของวัตถุพื้นฐานได้ง่ายกว่ามาก วิธีหนึ่งที่ทำได้กับแบบสอบถาม DB เดียว หลังจากนั้นฉันสร้างวัตถุคลาสย่อยโดยใช้วิธีการที่ระบุไว้ข้างต้น จากนั้นฉันสร้างแบบสอบถามที่สองและเติมค่าไดนามิกทั้งหมดในวัตถุคลาสย่อย
ผลลัพธ์สุดท้ายเป็นคลาสที่ได้รับพร้อมกับชุดตัวเลือกทั้งหมด การทำซ้ำสำหรับคลาสย่อยใหม่เพิ่มเติมจะใช้รหัสเพียงไม่กี่บรรทัด มันง่ายและใช้แพ็คเกจที่พยายามและทดสอบแล้ว (Newtonsoft) เพื่อให้เวทมนตร์ทำงานได้
โค้ดตัวอย่างนี้คือ vb.Net แต่คุณสามารถแปลงเป็น 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)
var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));
Newtonsoft.Json: ฉันจะใช้สิ่งนี้สำหรับการทดสอบหน่วยและ "การแฮ็ก" อื่น ๆ ที่ไม่ใช่การผลิตเท่านั้น!
คุณสามารถใช้ 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));
}
ในรหัส:
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);
}
อาจไม่เกี่ยวข้อง แต่ฉันสามารถรันโค้ดบนวัตถุที่ได้รับจากฐานของมัน แน่นอนว่ามันแฮ็คมากกว่าที่ฉันต้องการ แต่ใช้งานได้:
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);
คุณสามารถทำได้โดยใช้ทั่วไป
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; }
}
คุณจะได้รับประโยชน์สามประการโดยใช้แนวทางนี้
ฉันรู้ว่ามันเก่า แต่ฉันก็ใช้มันสำเร็จมาระยะหนึ่งแล้ว
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);
}
}
ฉันรวมบางส่วนของคำตอบก่อนหน้านี้ (ขอบคุณผู้เขียนเหล่านั้น) และรวบรวมคลาสแบบคงที่ง่ายๆด้วยสองวิธีที่เราใช้
ใช่มันง่ายไม่มันไม่ได้ครอบคลุมทุกสถานการณ์ใช่มันสามารถขยายและทำให้ดีขึ้นได้ไม่มันไม่สมบูรณ์แบบใช่มันอาจจะทำให้มีประสิทธิภาพมากขึ้นไม่ใช่ไม่ใช่สิ่งที่ดีที่สุดตั้งแต่ขนมปังหั่นบาง ๆ ใช่มี เครื่องมือแมปอ็อบเจ็กต์แพ็คเกจ nuget ที่มีประสิทธิภาพเต็มรูปแบบมีวิธีที่ดีกว่าสำหรับการใช้งานหนัก ฯลฯ ฯลฯ yada yada - แต่ใช้ได้กับความต้องการพื้นฐานของเรา :)
และแน่นอนมันจะพยายามแมปค่าจากวัตถุใด ๆ กับวัตถุใด ๆ โดยได้รับมาหรือไม่ก็ได้ (เฉพาะคุณสมบัติสาธารณะที่มีชื่อเหมือนกันเท่านั้น - ละเว้นส่วนที่เหลือ)
การใช้งาน:
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);
คลาสยูทิลิตี้แบบคงที่:
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);
}
});
}
}
คุณสามารถใช้ตัวสร้างการคัดลอกที่เรียกใช้ตัวสร้างอินสแตนซ์ทันทีหรือถ้าตัวสร้างอินสแตนซ์ของคุณทำมากกว่าการกำหนดให้ตัวสร้างการคัดลอกกำหนดค่าขาเข้าให้กับอินสแตนซ์
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; }
}
อ้างถึงเอกสารMicrosoft C # ภายใต้ Constructorสำหรับตัวอย่างนี้เคยมีปัญหานี้ในอดีต
อีกวิธีหนึ่งคือการเพิ่มวิธีการขยายดังนี้:
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;
}
}
จากนั้นมีตัวสร้างในแต่ละคลาสที่ได้รับที่ยอมรับคลาสฐาน:
public class DerivedClass: BaseClass
{
public DerivedClass(BaseClass baseModel)
{
this.CopyProperties(baseModel);
}
}
นอกจากนี้ยังจะเขียนทับคุณสมบัติปลายทางหากตั้งค่าไว้แล้ว (ไม่ใช่ null) หรือไม่ก็ได้
เป็นไปได้หรือไม่ที่จะกำหนดอ็อบเจ็กต์คลาสพื้นฐานให้กับการอ้างอิงคลาสที่ได้รับด้วยตัวพิมพ์ที่ชัดเจนใน C #?
ไม่เพียง แต่ชัดเจน แต่ยังสามารถแปลงโดยนัยได้อีกด้วย
ภาษา C # ไม่อนุญาตให้ใช้ตัวดำเนินการแปลงดังกล่าว แต่คุณยังสามารถเขียนได้โดยใช้ C # แท้และใช้งานได้ โปรดสังเกตว่าคลาสที่กำหนดตัวดำเนินการแปลงโดยนัย ( Derived
) และคลาสที่ใช้ตัวดำเนินการ ( Program
) ต้องถูกกำหนดในแอสเซมบลีที่แยกจากกัน (เช่นDerived
คลาสอยู่ในคลาสlibrary.dll
ที่อ้างอิงโดยprogram.exe
มีProgram
คลาส)
//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.
}
}
เมื่อคุณอ้างอิงไลบรารีโดยใช้การอ้างอิงโครงการใน Visual Studio VS จะแสดง squiggles เมื่อคุณใช้การแปลงโดยนัย แต่คอมไพล์ได้ดี หากคุณเพียงแค่อ้างอิงlibrary.dll
ไม่มี squiggles
System.Runtime.CompilerServices.SpecialName
Attribute ทำอะไร? เอกสารสำหรับทุกเวอร์ชันตั้งแต่รุ่นแรกสุดที่มี (2.0) ถึง "เวอร์ชันปัจจุบัน" (4.6? "ใคร? ใคร?") ไม่ได้บอกว่าทำอะไร แต่บอกว่า "ขณะนี้คลาส SpecialNameAttribute ไม่ได้ใช้ใน. NET Framework แต่สงวนไว้สำหรับใช้ในอนาคต ". ดู: [ลิงค์] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx )
where T : Delegate
คุณสมบัติเหมือนหรือพารามิเตอร์ที่เรียกว่าตัวทำดัชนี ฯลฯ ฯลฯ )
what does System.Runtime.CompilerServices.SpecialName Attribute do?
- ใช้เพื่อทำเครื่องหมายวิธีการที่สร้างขึ้นโดยโครงสร้างอำนวยความสะดวกพิเศษบางอย่างของภาษา. Net ระดับสูง: ตัวเข้าถึงคุณสมบัติ, ตัวเข้าถึงเหตุการณ์, ตัวสร้าง, ตัวดำเนินการ, ตัวทำดัชนี ฯลฯ เว้นแต่specialname
จะไม่เห็นวิธีการของ IL เป็นคุณสมบัติ / เหตุการณ์ / ตัวสร้างและจะได้รับการยอมรับว่าเป็นวิธีการปกติ การทำเครื่องหมายเมธอดที่ตั้งชื่ออย่างเหมาะสมด้วยตนเองด้วยแอ็ตทริบิวต์นี้เป็นเพียงการทำงานของคอมไพเลอร์ด้วยตนเอง
op_Exponent
วิธีการและทำเครื่องหมายด้วยspecialname
แอตทริบิวต์
เกี่ยวกับ:
public static T As<T>(this object obj)
{
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
}
วิธีที่ดีที่สุดในการเพิ่มคุณสมบัติพื้นฐานทั้งหมดให้กับรายการที่ได้รับคือใช้การสะท้อนใน costructor ลองใช้รหัสนี้โดยไม่ต้องสร้างเมธอดหรืออินสแตนซ์
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) { }
}
}
ฉันไม่เห็นด้วยที่มันเป็นไปไม่ได้ คุณสามารถทำได้ดังนี้:
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;
}
การใช้งาน:
var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);
var auto =
ยังคงเป็นประเภทsedan
นี่คือวิธีที่ฉันแก้ไขปัญหานี้สำหรับฟิลด์ คุณสามารถทำซ้ำแบบเดิมผ่านคุณสมบัติได้หากต้องการ คุณอาจต้องการตรวจสอบnull
ฯลฯ แต่นี่เป็นแนวคิด
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;
}
คุณสามารถจัดลำดับออบเจ็กต์พื้นฐานให้เป็น JSON จากนั้นจึงแยกซีเรียลเป็นอ็อบเจ็กต์ที่ได้รับ
ไม่ได้อยู่ในความรู้สึกดั้งเดิม ... แปลงเป็น Json จากนั้นเป็นวัตถุของคุณและบูมเสร็จแล้ว! เจสซี่ข้างบนมีคำตอบโพสต์ก่อน แต่ไม่ได้ใช้วิธีการขยายเหล่านี้ซึ่งทำให้กระบวนการง่ายขึ้นมาก สร้างวิธีการขยายสองสามวิธี:
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);
}
ใส่ไว้ในกล่องเครื่องมือของคุณตลอดไปจากนั้นคุณสามารถทำได้ตลอดเวลา:
var derivedClass = baseClass.ConvertToJson().ConvertToObject<derivedClass>();
อาพลังของ JSON
มี gotcha สองสามอย่างที่ใช้แนวทางนี้: เรากำลังสร้างวัตถุใหม่ไม่ใช่การหล่อซึ่งอาจมีหรือไม่สำคัญก็ได้ ช่องส่วนตัวจะไม่ถูกถ่ายโอนตัวสร้างที่มีพารามิเตอร์จะไม่ถูกเรียกเป็นต้นเป็นไปได้ว่าจะไม่กำหนดลูก json บางตัว JsonConvert ไม่ได้จัดการสตรีม อย่างไรก็ตามหากคลาสของเราไม่ได้อาศัยฟิลด์ส่วนตัวและคอนสตรัคเตอร์นี่เป็นวิธีการที่มีประสิทธิภาพมากในการย้ายข้อมูลจากคลาสหนึ่งไปยังคลาสโดยไม่ต้องแมปและเรียกคอนสตรัคเตอร์ซึ่งเป็นเหตุผลหลักว่าทำไมเราถึงต้องการแคสต์ตั้งแต่แรก
ไม่เห็นคำถามนี้ที่ฉันถาม - อัปเดตใน. NET โดยใช้ข้อมูลทั่วไป
วิธีที่ดีที่สุดคือสร้างคอนสตรัคเตอร์เริ่มต้นบนคลาสสร้างแล้วเรียกใช้Initialise
เมธอด