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

时间:2014-01-07 23:48:08

标签: c# generics

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

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类或对磁盘的引用的人都很难知道如何正确使用该类型。

也无法将type参数约束为正在定义的类。奇怪的是,没有一种在基类中声明属性或方法的专门方法,其中编译时类型是派生类型的任何类型。

4 个答案:

答案 0 :(得分:1)

发布这个答案来设置一个基线,但正如我所说的,我不是这个奇怪的“让一个类将自己作为其基类的类型参数传递”的粉丝。

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

您可以使用Disk构造函数和protected dynamicExchange构造函数中使基线解决方案更容易阅读并避免类型检查{1}}致电:

abstract class Disk<T> where T : Disk<T> {
    protected Disk()
    {

    }

    public T RMA()
    {
        return RMAService.Exchange((dynamic)this);
    }
}

protected构造函数使class HDD : Disk<SSD>这样的类在编译时失败并且dynamic延迟Exchange方法重载匹配决策直到运行时,所以你将得到正确的(或者非拟合真this类型时出错。

答案 3 :(得分:0)

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

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#不支持covariant return types