繁体   English   中英

基类中的方法如何基于调用它的对象的类型返回更多派生的对象?

[英]How can a method in a base class return a more derived object based on the type of the object it is called on?

假设您有两个类,如下例所示。

您将如何修改SplitObject使其始终返回类型t的对象,例如在Main()中,它应返回DerivedClass类型的对象?

我猜该解决方案将涉及反思? 我还没有任何有关反射的知识,所以我不知道这将如何工作。

public class BaseClass
{
    float _foo;

    public BaseClass(float foo){_foo = foo}

    public BaseClass SplitObject()
    {
        Type t = GetType();

        // Do something with t 

        _foo = _foo/2f;
        return new BaseClass(_foo); // I want to construct an 
                                   // object of type t instead 
                                  // of type BaseClass
    }
}

public class DerivedClass : BaseClass
{
    public DerivedClass(float foo) : base(foo){}
}

class Program
{
    static void Main() 
    {
        BaseClass foo = new DerivedClass(1f);

        BaseClass bar = foo.SplitObject(); // should return a DerivedObject
    }
}

无需反射-只需将SplitObject()设为虚拟,并在派生类中以不同的方式实现即可。

另一个选择是将Split行为提取到接口中,例如ISplittable<T>

public class BaseClass
{
      public virtual BaseClass SplitObject()
      {
           BaseClass splitObject = new BaseClass();
           //initialize the split object
           return splitObject;
      }
}

public class DerivedClass : BaseClass
{
     public override BaseClass SplitObject()
     {
          DerivedClass derivedSplitObject = new DerivedClass();
          //initialize the derived split object
          return derivedSplitObject;
     }
}

}

如果您真的想使用反射,则可以执行以下操作:

return (BaseClass)Activator.CreateInstance(GetType(), _foo);

当然,现在有一个隐式契约,所有派生类都必须实现这样的构造函数。 不幸的是,此类合同无法在当前的类型系统中指定; 因此在编译时不会发现违规。 埃拉什的想法会更好。 我会做类似的事情:

//... Base class:   

public BaseClass SplitObject()
{      
    _foo = _foo / 2f;
    return NewInstance(_foo);
}

protected virtual BaseClass NewInstance(float foo)
{
   return new BaseClass(foo);   
}

//... Derived class:

protected override BaseClass NewInstance(float foo)
{
   return new DerivedClass(foo);
}

如果只希望代码出现在一个位置(更好的维护方式,尤其是在派生类型很多的情况下),则需要使用反射:

  public class BaseClass
{
    float _foo;

    public BaseClass(float foo){_foo = foo;}

    public BaseClass SplitObject()
    {
        Type t = GetType();
        _foo = _foo / 2f;

        //Find the constructor that accepts float type and invoke it:
        System.Reflection.ConstructorInfo ci = t.GetConstructor(new Type[]{typeof(float)});
        object o=ci.Invoke(new object[]{_foo});

        return (BaseClass)o; 
    }
}

public class DerivedClass : BaseClass
{
    public DerivedClass(float foo) : base(foo) { }
}

class Program
{
    static void Main()
    {
        BaseClass foo = new DerivedClass(1f);

       //Cast the BaseClass to DerivedClass:
        DerivedClass bar = (DerivedClass)foo.SplitObject(); 
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM