是否有可能分配一个基类对象的派生类引用与明确的types转换在C#中?
是否有可能分配一个基类对象的派生类引用与明确的types转换在C#中?
我试过了,它创build了一个运行时错误。
不。对派生类的引用实际上必须引用派生类的实例(或空值)。 否则,你会如何预期它的行为?
例如:
object o = new object(); string s = (string) o; int i = s.Length; // What can this sensibly do?
如果您希望能够将基本types的实例转换为派生types,我build议您编写一个方法来创build适当的派生types实例。 或者再看一下你的inheritance树,并尝试重新devise,这样你就不需要这样做了。
不,这是不可能的,因为将它分配给派生类的引用就像是说“基类是派生类的完全有效的替代品,它可以做派生类可以做的所有事情”,这是不正确的,因为派生类一般提供更多的function比他们的基类(至less,这是inheritance的理念)。
你可以在派生类中编写一个构造函数,以基类对象作为参数,复制值。
像这样的东西:
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 } }
在这种情况下,您将复制基础对象并获取具有派生成员的默认值的function完整的派生类对象。 这样你也可以避免Jon Skeet指出的问题:
Base b = new Base(); Dervided d = new Derived(); 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!
我有这个问题,并通过添加一个方法来解决它,它接受一个types参数,并将当前对象转换为该types。
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
不,这是不可能的,因此你的运行时错误。
但是,您可以将派生类的实例分配给基类types的variables。
正如大家在这里所说的那样,这是不可能的。
我更喜欢的方法是相当干净的,就是使用像AutoMapper这样的Object Mapper。
它将自动执行将属性从一个实例复制到另一个实例(不一定是相同types)的任务。
和其他许多人一样,
当我需要使用基types作为派生types时,我在那些不幸的场合使用下面的代码。 是的,这违反了Liskov替代原则(LSP),大多数情况下我们赞成inheritance。 道具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>()
由于它使用reflection,它是“昂贵的”。 相应地使用。
扩展@ ybo的答案 – 这是不可能的,因为基类的实例实际上不是派生类的实例。 它只知道基类的成员,并不知道派生类的成员。
可以将派生类的实例转换为基类实例的原因是,派生类实际上已经是基类的实例,因为它已经有了这些成员。 相反,不能说。
您可以将一个作为基类input的variables强制转换为派生类的types; 然而,必要的话,这将做一个运行时检查,看看实际的对象是否是正确的types。
一旦创build,对象的types不能改变(特别是,它可能不是相同的大小)。 但是,您可以转换一个实例,创build第二个types的新实例 – 但是您需要手动编写转换代码。
不,这是不可能的。
考虑一个ACBus是基类Bus的派生类的情景。 ACBus具有TurnOnAC和TurnOffAC等function,它们在名为ACState的字段上运行。 TurnOnAC将ACState设置为打开,TurnOffAC将ACState设置为closures。 如果您尝试在总线上使用TurnOnAC和TurnOffACfunction,则没有任何意义。
另一个解决scheme是像这样添加扩展方法:
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); } }
如果已设置(不为空),也可以select覆盖目标属性。
可能不是相关的,但我能够在派生的对象的基础上运行代码。 这绝对是比我想要的更黑,但它的工作原理:
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);
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"); } }
}
当我们创build一个子类对象时,基类对象是自动启动的,所以基类引用variables可以指向子类对象。
但反之亦然,因为子类引用variables不能指向基类对象,因为没有创build子类对象。
也注意到基类引用variables只能调用基类成员。
是否有可能分配一个基类对象的派生类引用与明确的types转换在C#中?
不仅显式,而且隐式转换是可能的。
C#语言不允许这样的转换操作符,但是您仍然可以使用纯C#编写它们并且它们可以工作。 请注意,定义隐式转换运算符( Derived
)的类和使用运算符( Program
)的类必须在单独的程序集中定义(例如, Derived
类位于program.exe
所引用的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中使用Project Reference引用库时,VS在使用隐式转换时显示为波形,但编译得很好。 如果你只是参考library.dll
,没有波形。
实际上有办法做到这一点。 想想你如何使用Newtonsoft JSON从json反序列化一个对象。 它将(或者至less可以)忽略缺失的元素并填充它所知道的所有元素。
所以我就是这样做的 一个小的代码示例将遵循我的解释。
-
从基类创build一个对象的实例,并相应地填充它。
-
使用Newtonsoft json的“jsonconvert”类,将该对象序列化为一个jsonstring。
-
现在通过使用在步骤2中创build的jsonstring进行反序列化来创build您的子类对象。这将创build具有基类的所有属性的子类实例。
这就像一个魅力! 那么..什么时候这个有用? 有些人问这是否合理,并build议改变OP的模式,以适应这样的事实,即不能在类中inheritance(在.Net中)。
在我的情况下,我有一个设置类,其中包含一个服务的所有“基本”设置。 特定的服务有更多的select,这些选项来自不同的数据库表,所以这些类inheritance基类。 他们都有不同的select。 因此,当检索服务的数据时,首先使用基础对象的实例来填充值是非常容易的。 一种方法可以通过一个数据库查询来完成。 之后,我使用上面概述的方法创build子类对象。 然后,我做第二个查询,并填充子类对象上的所有dynamic值。
最终的输出是一个包含所有选项的派生类。 重复这个额外的新子类只需要几行代码。 这很简单,它使用了一个非常经得起考验的软件包(牛顿软件)来制作神奇的作品。
这个例子代码是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)
你可以使用generics来做到这一点。
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; } }
使用这种方法你有三个好处。
- 你没有复制代码
- 你不使用reflection(这是慢的)
- 所有的转换都在一个地方
不,看到我问的这个问题 – 使用generics在.NET中上传
最好的方法是在该类上创build一个默认的构造函数,然后构造并调用一个Initialise
方法