开发者

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

开发者 https://www.devze.com 2023-01-24 06:17 出处:网络
Suppose you have two classes, as in the example below. How would you modify SplitObject such that it always returns an object of type t, such as in Main(), where it should return an object of type De

Suppose you have two classes, as in the example below.

How would you modify SplitObject such that it always returns an object of type t, such as in Main(), where it should return an object of type DerivedClass?

I'm guessing the solution would involve reflection? I haven't learned anything about reflection yet, so I don't know how this would work.

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 DerivedCl开发者_StackOverflow社区ass(1f);

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


If you really wanted to use reflection, you could do something like:

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

Of course, there is now an implicit contract that all derived classes must implement such a constructor. Unfortunately, such contracts cannot be specified in the current type-system; so violations will not be caught at compile-time. It would be much better to go with erash's idea. I would do something like:

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


No reflection required -- just make SplitObject() be virtual and implement it differently in your derived classes.

Another option would be to extract the Split behavior into an interface, say 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;
     }
}

}


If you only want the code to appear in one place (better for maintenance, especially if there are many derived types), you will need to use reflection:

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

精彩评论

暂无评论...
验证码 换一张
取 消