2011-02-18 88 views
20
Class A { } 
Class B : A { } 

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

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

是否有可能做這樣的事情上面,並有編譯器打印出A型,而不是B型基本上是有可能永久性地投對象,因此「失去」的所有派生數據?永久投派生類基礎

+0

在答案中加入爲什麼你不能這樣做,你真的不應該這樣做。當你想知道一個對象是否是基類的一種類型時,你不必檢查運行時類型是否相等,但是你可以使用`is`或`IsAssignableFrom`等特性。 – 2011-02-18 22:52:32

+0

ItemA.GetType()。FullName輸出是什麼? `Namespace.B`? – 2011-02-18 22:55:08

+5

是的,那時ItemA是B.我真正想要解決的是一個WCF需要確切類型的問題。我知道我可以使用KnownTypes,但如果可能的話,我寧願不使用KnownTypes。 – Telavian 2011-02-18 22:59:34

回答

10

什麼你問的原因有兩個是不可能的:

  1. ItemA.GetType()不返回變量ItemA的編譯時類型 - 它返回的對象運行時類型簡稱通過ItemA
  2. 因爲用戶定義的轉換運算符(這裏您唯一的希望)不能從派生類轉換爲基類,所以您無法使(A)B導致表示更改轉換(即新的A對象)。你只是想得到一個正常的,安全的參考轉換。

那邊,你要求的是很奇怪的;人們會認爲你很難違反Liskov的替代原則。這裏幾乎肯定有一個嚴重的設計缺陷,您應該解決。

如果你還想這樣做,您可以編寫一種方法,手動構建B中的A,方法是創建A,然後複製數據。這可能以B上的ToA() 實例方法存在。

爲「?如何從現有一個構建一個」如果你將這個問題,這讓很多更有意義:創建A一個拷貝構造函數,其聲明如下public A(A a){...},這是不可知以子類特定的細節。這給你一個通用的方法來創建A現有的A或它的一個子類實例。

1

不,您不能強制子類型的實例報告它的類型名稱是基類型。

請注意,當您使用指向B類實例的ItemA變量時,只能使用ItemA變量訪問A類中定義的字段和方法。在很少的地方,ItemA指向除類A以外的其他實例的事實實際上可以被觀察到 - 在子類中重寫的虛方法是一種情況,而對運行時類型本身的操作(如GetType())則是如此。

如果你問這個問題是因爲當你想要類A的一個實例時,它發送一個類B的實例時,某段代碼失敗了,這聽起來像你應該仔細看看那個代碼看看它做錯了什麼。如果它正在測試GetType.LastName,那麼它已經崩潰了,並且已經失去了靈感。如果它正在測試x IS A,那麼傳遞B的實例將通過,一切都應該罰款。

6

爲了娛樂,如果你想失去所有所得到的數據,你可以這樣做:

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; 
     } 
    } 
} 

如果您使用調試器,你會看到名字仍保存在該領域名字時,它是投到一個A,當你從WipeAllTracesOfB得到一個A時,沒有名字或任何B的蹤跡。

0

鑄造不會改變對象的類型。所有的轉換方式都是告訴編譯器什麼類型來處理對象(假設對象實際上是那種類型)。

A ItemA = (A)ItemB;是說拿ItemB,把它當作好像它是A類型,並將其命名爲ItemA

當你調用ItemA.GetType()你問真正的類型,不只是它被視爲類型,和真正的類型是B

(我改變了你的代碼示例什麼,我認爲你的意思,既然你不會編譯什麼)

2

(這可能是顯而易見的,但是......)

回覆:accepted answer by @Ani

如果你仍然想做到這一點;您可以編寫一種方法,通過創建A然後複製數據來手動從B構建A。這可能是一個的ToA()實例方法存在於B.

或者使用Automapper將數據複製回:

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

然後wipedInstance.GetType()typeof(BaseClass)

8

我最近跑將這個舊項目遷移到實體框架中。如前所述,如果您從實體獲得派生類型,則無法存儲它,而只能使用基本類型。 該解決方案是一種帶反射的擴展方法。

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; 
    } 

它不是太整齊,所以如果你真的沒有其他選擇,就用這個。