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

时间:2010-11-11 02:19:41

标签: c# oop reflection

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

如何修改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
    }
}

3 个答案:

答案 0 :(得分:1)

不需要反射 - 只需将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;
     }
}

}

答案 1 :(得分:1)

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

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

当然,现在有一个隐式契约,所有派生类都必须实现这样的构造函数。不幸的是,这种合同无法在当前的类型系统中指定;所以在编译时不会发现违规行为。使用erash的想法会好得多。我会做类似的事情:

//... 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);
}

答案 2 :(得分:1)

如果您只希望代码出现在一个地方(更适合维护,特别是如果有许多派生类型),您将需要使用反射:

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