Как метод в базовом классе может возвращать более производный объект, основанный на типе объекта, для которого он вызывается? - PullRequest
1 голос
/ 11 ноября 2010

Предположим, у вас есть два класса, как в примере ниже.

Как бы вы изменили 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 ]

1 голос
/ 11 ноября 2010

Если вы действительно хотите использовать отражение, вы можете сделать что-то вроде:

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);
}
1 голос
/ 11 ноября 2010

Если вы хотите, чтобы код отображался только в одном месте (лучше для обслуживания, особенно если есть много производных типов), вам нужно будет использовать отражение:

  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(); 
    }
}
1 голос
/ 11 ноября 2010

Не требуется никакого отражения - просто сделайте 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;
     }
}

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...