2016-10-28 24 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. 
    } 
}