关于c#:是否可以使用显式类型转换将基类对象分配给派生类引用?

Is it possible to assign a base class object to a derived class reference with an explicit typecast?

是否可以使用C中的显式类型转换将基类对象分配给派生类引用?.

我已经尝试过了,它创建了一个运行时错误。


不可以。对派生类的引用必须实际引用派生类的实例(或空)。否则你会期望它如何表现?

例如:

1
2
3
object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?

如果您希望能够将基类型的实例转换为派生类型,我建议您编写一个方法来创建适当的派生类型实例。或者再次查看您的继承树,尝试重新设计,这样您就不需要首先这样做。


不,这是不可能的,因为将其分配给派生类引用就像说"基类是派生类的完全可以替代的,它可以做派生类所能做的一切",这不是真的,因为派生类通常提供比其基类更多的功能(至少,这是继承背后的想法)。

可以在派生类中以基类对象为参数编写构造函数,并复制这些值。

像这样:

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
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!

我遇到了这个问题,并通过添加一个方法来解决它,该方法接受类型参数并将当前对象转换为该类型。

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语言编写它们,并且它们可以工作。请注意,定义隐式转换运算符(Derived和使用运算符(Program的类)的类必须在单独的程序集中定义(例如,Derived类位于library.dll中,由包含Program类的program.exe引用)。

    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会显示波形曲线,但编译起来很好。如果你只引用library.dll,就没有蠕动。


    可能不相关,但我能够在给定基的派生对象上运行代码。它肯定比我想要的更黑,但是它可以工作:

    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中使用泛型进行上推

    最好的方法是在类上创建一个默认的构造函数,构造然后调用一个Initialise方法