2016-10-28 32 views
12

我有包含以下C#程序的一個問題:如何向下轉換由靜態方法生成的實例?

class Program 
{ 
    static void Main(string[] args) 
    { 
     Child childInstance = Child.ParseFromA(@"path/to/Afile") as Child; 
    } 
} 

class Parent{ 
    int property; 

    public static Parent ParseFromA(string filename) 
    { 
     Parent parent = new Parent(); 
     // parse file and set property here... 
     return parent; 
    } 
} 

class Child : Parent 
{ 
    public void SomeAdditionalFunction() { } 
} 

當運行該代碼,childInstance變得null

我試過下面明確的轉換任務,但有一個例外結束:
Child childInstance = (Child)Child.ParseFromA(@"path/to/Afile");

因爲我想分析某些類型的文件到ParentChild情況下,我想保留通過生成實例設計靜態方法。

我該如何得到一個合適的childInstance

回答

23

你不能低調。一旦一個對象被創建爲Parent,它將總是Parent。這就像試圖將new object()降頻爲string:這樣做不起作用 - 該字符串應表示哪個字符序列?

因此,您唯一的解決方法是以創建正確的對象。我看到你的情況下,唯一的選擇就是讓你的靜態方法通用:

public static T ParseFromA<T>(string filename) where T : Parent, new() 
{ 
    T t = new T(); 
    // parse file and set property here... 
    return t; 
} 

用法:

Child childInstance = Parent.ParseFromA<Child>(@"path/to/Afile"); 

通用約束T : Parent確保TParent一個亞型,並new()確保T有一個無參數的構造函數。

+2

謝謝!你的解決方案正是我想要做的! 我應用了它,並完美的工作。 – Malboma99

+2

關於你的例子只是一個小問題:將一個'Animal'轉換成'Cat'是完全理智的:如果它是一個'Cat',你會收到一個'Cat',如果它不是(null)什麼作者)。這裏的問題是,如果你創建一個「動物」,你創建一個抽象的動物,不能成爲一隻貓。這是一個沒有一種類型的模型。如果你談論一個男人,你不是指「約翰」, - 你是指一個抽象的男人。我很確定那是有意的,但我認爲從你的例子來看有點不清楚。 – Archeg

+1

@Archeg:你當然是完全正確的。我修改了我的示例,並決定使用一些框架類。 – Heinzi

3

如果你堅持使用靜態方法,並且不希望使用反射或泛型,那麼你也可以考慮使用new關鍵字:

class Parent 
{ 
    public static Parent ParseFromA(string filename) 
    { 
     Parent parent = new Parent(); 
     parent.Parse(filename); 
     return parent; 
    } 

    protected virtual void Parse(string fileName) 
    { 
     ... 
    } 
} 

class Child : Parent 
{ 
    public new static Child ParseFromA(string filename) 
    { 
     Child child = new Child(); 
     child.Parse(filename); 
     return parent; 
    } 

    protected override void Parse(string fileName) 
    { 
     base.Parse(fileName); 
     SomeAdditionalFunction(); 
    } 
} 

個人而言,我只是將使用實例方法。

var child = new Child(...); 
child.Parse(...); 

額外的一行代碼是一個小代價清潔代碼,恕我直言。正如你所看到的,static關鍵字不能很好地繼承。您也可以隨時換實例方法爲擴展方法,如果你想要一個班輪畢竟:

public static class ParentEx 
{ 
    public static T ParseFile<T>(this T source, string fileName) : where T : Parent 
    { 
     source.Parse(fileName); 
     return source; 
    } 
} 

然後

var child = new Child().ParseFile(fileName); 
+0

或者在基類上使用虛擬,覆蓋子類 – Mafii

+0

@Mafii例如方法,是的。儘管如此,你不能覆蓋靜態方法。 –

2

如果您的靜態方法不知道要創建什麼類型的,你需要通過它。例如,使用泛型:

namespace ConsoleApplication18 
{ 
    class Program 
    { 
    static void Main(string[] args) 
    { 
     var childInstance = Parent.ParseAs<Child>(@"path/to/Afile"); 

     childInstance.SomeAdditionalFunction(); 
    } 
    } 

    class Parent 
    { 
    int property; 

    public static T ParseAs<T>(string filename) where T : Parent, new() 
    { 
     var parent = new T(); 

     // parse file and set property here... 
     parent.property = 42; 

     return parent; 
    } 
    } 

    class Child : Parent 
    { 
    public void SomeAdditionalFunction() { } 
    } 
} 
1

您只能強制轉換爲父類,而不轉換爲子類。編譯器無法安全地假定該對象已正確構建,並具有作爲子對象安全訪問的所有必需屬性。

可以使用Heinzi上面提到的通用方法,也可以在父類和子類中使用參數化構造函數和實例化解析方法。

class Parent 
{ 
    public Parent() { } 
    public Parent(string fileName) 
    { 
     Parse(fileName); 
    } 

    private void Parse(string fileName) 
    { 
     // Do your parsing stuff here. 
    } 
} 

class Child : Parent 
{ 
    public Child() { } 
    public Child(string fileName) : base(fileName) 
    { 
     // Parsing is done already done within the constructor of Parent, which is called by base(fileName) 
     // All you need to do here is initialize the rest of your child object. 
    } 
}