如何向下转换由静态方法生成的实例?

本文关键字:实例 静态方法 转换 | 更新日期: 2023-09-27 18:15:25

我有一个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 ?

如何向下转换由静态方法生成的实例?

不能向下转换。一旦对象被创建为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有一个无参数构造函数。

如果您坚持使用静态方法而不想使用反射或泛型,那么您也可以考虑使用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);

如果静态方法不知道要创建什么类型,则需要传递它。例如,使用泛型:

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() { }
  }
}

只能转换为父类,不能转换为子类。编译器不能安全地假设该对象已被正确构造,具有作为子对象可以安全访问的所有必要属性。

要么使用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.
    }
}