如何定义一个返回派生类类型实例的泛型方法



例如,我有两个类执行相同的功能:

class HDD : Disk
{
public HDD RMA()
{
HDD newHDD = RMAService.Exchange(this);
return newHDD;
}
}
class SSD : Disk
{
public SSD RMA()
{
SSD newSSD = RMAService.Exchange(this);
return newSSD;
}
}

我想在基类中实现RMA函数,但保持强类型返回值,这样用户就可以通过函数签名确定他们正在取回他们发送的磁盘类型!


到目前为止我尝试过的内容:

(见下面的解决方案)

不过,这种类型定义很难看。任何创建自己的Disk类或引用Disk的人都很难知道如何正确使用该类型。

也没有办法将类型参数约束为正被定义的类。奇怪的是,在编译时类型为派生类型的基类中,没有专门的方法来声明属性或方法

发布这个答案是为了设置一个基线,但正如我所说,我不喜欢这种奇怪的"让一个类将自己作为类型参数传递给它的基类"构造。

abstract class Disk<T> where T : Disk<T> //Ugly recursive type parameter
{
public Disk()
{
if( this.GetType() != typeof(T) ) //Ugly type check
throw new Exception("The type argument must be precisely the " +
"derived class you're defining. Annoying eh?")
}
public T RMA()
{
Disk<T> newDisk = RMAService.Exchange(this)
return (T)newDisk; //Ugly explicit cast
}
}

这让你可以去:

class HDD : Disk<HDD> { }  //Ugly self-referencing type parameter
class SSD : Disk<SSD> { }
HDD newHDD = someHDD.RMA();

我刚刚使用扩展方法提出了这个解决方案,看起来好多了。为什么它在编译时看起来不起作用?

public abstract class Disk
{
public Disk() { }
}
public static class Disk_Extension_Methods
{  
public static T RMA<T>(this T oldDisk) where T : Disk
{
T newDisk = RMAService.Exchange(oldDisk)
return newDisk;
}
}

这让你可以去:

public class HDD : Disk { }
public class SSD : Disk { }
HDD newHDD = someHDD.RMA();

您可以使用protected构造函数和Exchange调用上的dynamic使基线解决方案更易于阅读,并避免在Disk构造函数内进行类型检查:

abstract class Disk<T> where T : Disk<T> {
protected Disk()
{
}
public T RMA()
{
return RMAService.Exchange((dynamic)this);
}
}

protected构造函数使像class HDD : Disk<SSD>这样的类在编译时失败,而dynamicExchange方法重载匹配决策延迟到运行时,所以您会得到正确的结果(或者在不适合实际this类型时出错)。

不确定这是否是您想要的,但请尝试将基类中的(隐藏)实现与派生子类中的公共接口方法分离。

abstract class Disk    //base class
{
protected Disk CreateRMA()
{
Disk  newDisk;
newDisk = new RMAService.Exchange(this);
...whatever else needs to be done...
return newDisk;
}
}
class HDD: Disk
{
public HDD RMA()
{
return CreateRMA();
}
}
class SSD: Disk
{
public SSD RMA()
{
return CreateRMA();
}
}

每个派生类的RMA()方法只需调用基类的CreateRMA()方法,然后将生成的Disk对象强制转换为派生类类型。每个返回的值都有适当的子类型,每个RMA()方法的所有实际工作都在基类方法中完成。

现在你可以做这些:

HDD  disk1 = someHDD.RMA();
SSD  disk2 = someSSD.RMA();

附录

这并不能解决OP的问题,因为对RMAService.Exchange()的调用应该特定于(重载)每个派生的子类型。一种不同的方法将该调用保留在派生类型的方法中,但允许基类完成所需的所有其他初始化工作:

// Second approach
abstract class Disk    //base class
{
protected Disk CreateRMA(Disk newDisk)
{
...other initialization code...
return newDisk;
}
public abstract Disk RMA();    //not sure if this works
}
class HDD: Disk
{
public override HDD RMA()
{
return CreateRMA(new RMAService.Exchange(this));
}
}
class SSD: Disk
{
public override SSD RMA()
{
return CreateRMA(new RMAService.Exchange(this));
}
}

对于派生类,这需要做更多的工作,但仅限于重载的new调用;所有其余的初始化代码都保留在基类方法上。

投降

好吧,这也不起作用,因为C#不支持协变返回类型。

最新更新