簡體   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