Permanently cast derived class to base

前端 未结 6 1538
慢半拍i
慢半拍i 2020-12-09 16:19
Class A { }
Class B : A { }

B ItemB = new B();
A ItemA = (A)B;

Console.WriteLine(ItemA.GetType().FullName);

Is it possible to do something like a

相关标签:
6条回答
  • 2020-12-09 16:51

    For amusement, if you wanted to lose all of the derived data you could do this:

       class Program
        {
            [DataContract(Name = "A", Namespace = "http://www.ademo.com")]
            public class A { }
             [DataContract(Name = "A", Namespace = "http://www.ademo.com")]
            public class B : A  {
                 [DataMember()]
                 public string FirstName;
            }  
    
        static void Main(string[] args)
        {
            B itemB = new B();
            itemB.FirstName = "Fred";
            A itemA = (A)itemB; 
            Console.WriteLine(itemA.GetType().FullName);
            A wipedA = WipeAllTracesOfB(itemB);
            Console.WriteLine(wipedA.GetType().FullName);
        }
    
        public static A WipeAllTracesOfB(A a)
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(A));
    
            using (MemoryStream ms = new MemoryStream())
            {
                serializer.WriteObject(ms, a);
                ms.Position = 0;
    
                A wiped = (A)serializer.ReadObject(ms);
    
                return wiped;
            }
        }
    }
    

    If you use the debugger you will see the FirstName is still stored in the field FirstName when it is cast to an A, when you get an A back from WipeAllTracesOfB there is no FirstName, or any trace of B.

    0 讨论(0)
  • 2020-12-09 16:53

    I've recently run into this migrating an old project to Entity Framework. As it was mentioned, if you have a derived type from an entity, you can't store it, only the base type. The solution was an extension method with reflection.

        public static T ForceType<T>(this object o)
        {
            T res;
            res = Activator.CreateInstance<T>();
    
            Type x = o.GetType();
            Type y = res.GetType();
    
            foreach (var destinationProp in y.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
            {
                var sourceProp = x.GetProperty(destinationProp.Name);
                if (sourceProp != null)
                {
                    destinationProp.SetValue(res, sourceProp.GetValue(o));
                }
            }
    
            return res;
        }
    

    It's not too neat, so use this if you really have no other option.

    0 讨论(0)
  • 2020-12-09 17:07

    What you ask for is impossible for two reasons:

    1. ItemA.GetType() does not return the compile-time type of the variable ItemA - it returns the run-time type of the object referred to by ItemA.
    2. There's no way you could make (A)B result in a representation-changing conversion (i.e. a new A object) because user-defined conversion operators (your only hope here) cannot convert from derived to base-classes. You're just going to get a normal, safe, reference-conversion.

    That aside, what you ask for is very strange; one would think you're trying really hard to violate Liskov's substiution principle. There's almost certainly a serious design-flaw here that you should address.

    If you still want to do this; you could write a method that manually constructs an A from a B by newing up an A and then copying data over. This might exist as a ToA() instance-method on B.

    If you characterized this problem as "How do I construct an A from an existing A?", it makes a lot more sense: create a copy-constructor on A, whose declaration looks like public A(A a){...}, which is agnostic to subclass-specific details. This gives you a general means to create an A from an existing instance of A or one of its subclasses.

    0 讨论(0)
  • 2020-12-09 17:13

    Casting does not change the type of the object. All casting means is you telling the compiler what type to treat the object as (assuming the object actually is that type).

    A ItemA = (A)ItemB; is saying take ItemB, treat it as if it were of type A, and call it ItemA

    When you call ItemA.GetType() you're asking for the real type, not just the type it's being treated as, and the real type is B

    (I changed your code sample to what I think you meant, since what you have won't compile)

    0 讨论(0)
  • 2020-12-09 17:14

    No, you can't force an instance of a child type to report that it's type name is the base type.

    Note that when you are using the ItemA variable pointing to the instance of class B, you can only access the fields and methods defined in class A using the ItemA variable. There are very few places where the fact that ItemA points to an instance of something other than class A can actually be observed - virtual methods overridden in the child classes is one case, and operations on the runtime type itself, such as GetType().

    If you're asking this question because some piece of code is failing when you send it an instance of class B when it is expecting an instance of class A, it sounds like you should be taking a closer look at that code to see what it's doing wrong. If it's testing GetType.LastName, then it's broken and braindead. If it's testing x IS A then passing an instance of B will pass and everything should be fine.

    0 讨论(0)
  • 2020-12-09 17:15

    (this is probably obvious, but...)

    re: the accepted answer by @Ani:

    If you still want to do this; you could write a method that manually constructs an A from a B by newing up an A and then copying data over. This might exist as a ToA() instance-method on B.

    Or use Automapper to copy the data back:

    Mapper.CreateMap<ChildClass, BaseClass>();
    // ...later...
    var wipedInstance = Mapper.Map<BaseClass>(instanceOfChildClass);
    

    then wipedInstance.GetType() would be typeof(BaseClass)

    0 讨论(0)
提交回复
热议问题