Is it possible to assign a base class object to a derived class reference with an explicit typecast?
是否可以使用C中的显式类型转换将基类对象分配给派生类引用?.
我已经尝试过了,它创建了一个运行时错误。
不可以。对派生类的引用必须实际引用派生类的实例(或空)。否则你会期望它如何表现?
例如:
1 2 3 |
如果您希望能够将基类型的实例转换为派生类型,我建议您编写一个方法来创建适当的派生类型实例。或者再次查看您的继承树,尝试重新设计,这样您就不需要首先这样做。
不,这是不可能的,因为将其分配给派生类引用就像说"基类是派生类的完全可以替代的,它可以做派生类所能做的一切",这不是真的,因为派生类通常提供比其基类更多的功能(至少,这是继承背后的想法)。
可以在派生类中以基类对象为参数编写构造函数,并复制这些值。
像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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指出的问题:
1 2 3 4 5 6 7 8 9 10 |
我遇到了这个问题,并通过添加一个方法来解决它,该方法接受类型参数并将当前对象转换为该类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } |
这意味着您可以在这样的代码中使用它:
1 2 3 4 | var base = new Base(); base.Data = 1; var derived = base.As<Derived>(); Console.Write(derived.Data); // Would output 1 |
。
正如许多其他人回答的那样,没有。
当需要将基类型用作派生类型时,我会在那些不幸的情况下使用以下代码。是的,这违反了Liskov替换原则(LSP),是的,大多数时候我们更喜欢组合而不是继承。给马库斯·克纳本约翰森的道具,他最初的答案是基于这个。
基类中的此代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } |
。
允许:
1 | derivedObject = baseObect.As<derivedType>() |
。
因为它使用反射,所以它是"昂贵的"。相应地使用。
不,这是不可能的,因此您的运行时错误。
但可以将派生类的实例赋给基类类型的变量。
正如这里所有人所说,这不可能直接实现。
我更喜欢并且相当干净的方法是使用像automapper这样的对象映射器。
它将自动执行将属性从一个实例复制到另一个实例(不一定是同一类型)的任务。
可以将类型化为基类的变量强制转换为派生类的类型;但是,根据需要,这将进行运行时检查,以查看所涉及的实际对象的类型是否正确。
一旦创建,就不能更改对象的类型(不仅如此,它的大小可能不同)。但是,您可以转换一个实例,创建第二种类型的新实例,但是您需要手动编写转换代码。
不,这是不可能的。
考虑一个场景,其中acbus是基类总线的派生类。acbus具有turnonac和turnoffac等特性,可以在名为acstate的字段上运行。turnonac将acstate设置为on,turnoffac将acstate设置为off。如果你试图在公共汽车上使用turnonac和turnoffac功能,那是没有意义的。
扩展@ybo的答案-这是不可能的,因为您拥有的基类实例实际上不是派生类的实例。它只知道基类的成员,不知道派生类的成员。
之所以可以将派生类的实例强制转换为基类的实例,是因为派生类实际上已经是基类的实例,因为它已经具有这些成员。不能说是相反的。
实际上有一种方法可以做到这一点。考虑如何使用NewtonSoft JSON从JSON反序列化对象。它将(或者至少可以)忽略缺少的元素并填充它知道的所有元素。
我就是这么做的。一个小代码示例将遵循我的解释。
从基类创建对象的实例并相应地填充它。
使用newtonsoft json的"jsonconvert"类,将该对象序列化为一个json字符串。
现在,通过使用步骤2中创建的JSON字符串反序列化来创建子类对象。这将创建具有基类所有属性的子类实例。
这很有魅力!所以…什么时候有用?有些人问这什么时候会有意义,并建议更改OP的模式以适应这样一个事实,即您不能使用类继承(在.NET中)进行本机操作。
在我的例子中,我有一个包含服务的所有"基本"设置的设置类。特定的服务有更多的选项,而这些选项来自不同的DB表,因此这些类继承了基类。他们都有不同的选择。因此,在检索服务的数据时,首先使用基对象的实例填充值要容易得多。一种方法可以通过单个数据库查询来实现这一点。在这之后,我使用上面概述的方法创建子类对象。然后我进行第二个查询并填充子类对象上的所有动态值。
最终输出是一个具有所有选项集的派生类。为其他新的子类重复这个过程只需要几行代码。它很简单,而且它使用了一个经过反复测试的软件包(newtonsoft)来让魔法发挥作用。
这个示例代码是vb.net,但是您可以很容易地转换为c。
1 2 3 4 5 6 | ' 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) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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"); } } |
}
当我们创建一个子类对象时,基类对象是自动启动的,因此基类引用变量可以指向子类对象。
但反之亦然,因为子类引用变量不能指向基类对象,因为没有创建子类对象。
还请注意,基类引用变量只能调用基类成员。
我知道这是旧的,但我已经成功地用了一段时间了。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } } |
号
您可以使用扩展名:
1 2 3 4 5 6 | 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)); } |
在代码中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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); } |
。
Is it possible to assign a base class object to a derived class reference with an explicit typecast in C#?.
号
不仅可以进行显式转换,还可以进行隐式转换。
C语言不允许这样的转换操作符,但是您仍然可以使用纯C语言编写它们,并且它们可以工作。请注意,定义隐式转换运算符(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //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会显示波形曲线,但编译起来很好。如果你只引用
可能不相关,但我能够在给定基的派生对象上运行代码。它肯定比我想要的更黑,但是它可以工作:
1 2 3 4 | public static T Cast<T>(object obj) { return (T)obj; } |
…
1 2 3 4 5 | //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); |
。
向派生项添加所有基属性的最佳方法是在costructor中使用反射。尝试此代码,而不创建方法或实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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) { } } } |
。
您可以使用generic来完成此操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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; } } |
。
使用这种方法有三个好处。
我结合了前面的部分答案(感谢那些作者),并用我们正在使用的两个方法组成了一个简单的静态类。
是的,它很简单,不,它不覆盖所有的场景,是的,它可以被扩展并变得更好,不,它不是完美的,是的,它可能会变得更高效,不,这不是切片面包以来最伟大的事情,是的,那里有很多强大的Nuget包对象映射器,它们对于大量使用来说是更好的,等等,Yada Yada-但是它可以工作。但是,为了我们的基本需要:)
当然,它会尝试将值从任何对象映射到任何派生对象或非派生对象(当然,只有命名相同的公共属性才会忽略其余属性)。
用途:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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); |
静态实用程序类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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); } }); } } |
号
怎么样:
1 2 3 4 | public static T As<T>(this object obj) { return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj)); } |
另一个解决方案是添加这样的扩展方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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; } } |
然后在每个接受基类的派生类中都有一个构造函数:
1 2 3 4 5 6 7 | public class DerivedClass: BaseClass { public DerivedClass(BaseClass baseModel) { this.CopyProperties(baseModel); } } |
号
如果已经设置(不是空值),它还可以覆盖目标属性。
不,请看我问的这个问题-在.NET中使用泛型进行上推
最好的方法是在类上创建一个默认的构造函数,构造然后调用一个