C# 约束中的泛型协方差

C# 约束中的泛型协方差,c#,generics,C#,Generics,我目前在C#中遇到了泛型问题,我有一个解决方案,但我正在寻找一个更干净的解决方案。我通常有很好的C#知识,所以我不确定我想要的东西是否存在 我有一个带有嵌套约束的泛型基类 public abstract class BaseParam { } public abstract class BaseProcess<TParam> : IProcess where TParam : BaseParam { public TParam Parameter { get; set

我目前在C#中遇到了泛型问题,我有一个解决方案,但我正在寻找一个更干净的解决方案。我通常有很好的C#知识,所以我不确定我想要的东西是否存在

我有一个带有嵌套约束的泛型基类

public abstract class BaseParam { }

public abstract class BaseProcess<TParam> : IProcess
   where TParam : BaseParam
{
    public TParam Parameter { get; set; }
}

public abstract class BaseTask<TProc, TParam>
   where TProc : BaseProcess<TParam>, new()
   where TParam : BaseParam, new()
{
    public IProcess Create()
    {
        var proc = new TProc();
        proc.Param = new TParam();
        return proc;
    }
}
公共抽象类BaseParam{}
公共抽象类BaseProcess:IProcess
其中TParam:BaseParam
{
公共TParam参数{get;set;}
}
公共抽象类基任务
其中TProc:BaseProcess,new()
其中TParam:BaseParam,new()
{
公共IProcess Create()
{
var proc=new TProc();
proc.Param=新的TParam();
返回过程;
}
}
现在,我可以创建一些派生,这在一段时间内有效

public class SomeParam : BaseParam { }

public class SomeProcess : BaseProcess<SomeParam> { }

public class SomeTask : BaseTask<SomeProcess, SomeParam> { }
public类SomeParam:BaseParam{}
公共类SomeProcess:BaseProcess{}
公共类SomeTask:BaseTask{}
当我开始像这样混合继承级别时,问题就出现了

public class SuperParam : SomeParam { }

// The next line won't compile
public class SuperTask : BaseTask<SomeProcess, SuperParam> { }
公共类超级参数:SomeParam{}
//下一行无法编译
公共类超级任务:BaseTask{}

我知道它不会编译,因为协方差只存在于接口上,
Foo
Foo
在技术上是两个不同的类。不过,里面的代码可以正常工作。我想写一个“协变约束”,比如:

公共抽象类BaseTask
其中TProc:BaseProcess
其中TProcParam:BaseParam
其中TParam:TProcParam
{
}
我尝试使用
isomproc
,因为接口是协变的,但这只适用于赋值,而不适用于下界。函数解决方法是声明一个额外的类型参数,但这还不够:

public abstract class BaseTask<TProc, TProcParam, TParam>
   where TProc : BaseProcess<TProcParam>
   where TProcParam : BaseParam
   where TParam : TProcParam
{
}
公共抽象类BaseTask
其中TProc:BaseProcess
其中TProcParam:BaseParam
其中TParam:TProcParam
{
}
编译器错误:

The type 'SomeProcess' cannot be used as type parameter 'TProc' in the
generic type or method 'BaseTask<TProc,TParam>'. There is no implicit
reference conversion from 'SomeProcess' to 'BaseProc<SuperParam>'.  
类型“SomeProcess”不能用作
泛型类型或方法“BaseTask”。没有隐含的
从“SomeProcess”到“BaseProc”的引用转换。
编辑:我知道需要更多的解释。在类中,我不需要
SuperParam
的属性,只需要
BaseParam
,但它应该是
SuperParam
的一个实例。但是,我们通过反射读取
超级任务
定义并创建一个编辑器。编辑器必须知道应该创建什么对象


问题:是否有办法设计
BaseTask
的约束,使其在不添加额外参数的情况下尊重协方差?我可以想出解决办法,但我觉得有一种更简单的方法。

解决问题的方法在很大程度上取决于类的实现方式,但如果您可以将
BaseProcess
转换为一个共变量接口
IBaseProcess

然后,
SomeProcess
as:

public abstract class BaseTask<TProc, TParam>
    where TProc : IBaseProcess<BaseParam>
    where TParam : BaseParam
{
}
public class SomeProcess : IBaseProcess<SomeParam> { }

根据评论和进一步检查,我注意到两件事:

  • 这个例子是不完整的,这分散了对手头问题的注意力
  • 我想做的是编译器无法理解的
  • 我现在将采用的解决方案是解决方案和保持默认行为干净的努力的结合:

    // Use workaround on BaseTask
    public abstract class BaseTask<TProc, TProcParam, TParam>
       where TProc : BaseProcess<TProcParam>
       where TProcParam : BaseParam
       where TParam : TProcParam
    {
    }
    
    // Generic shortcut overload
    public abstract class BaseTask<TProc, TParam> : BaseTask<TProc, TParam, TParam>
       where TProc : BaseProcess<TProcParam>
       where TParam : TProcParam
    {
    }
    
    //在BaseTask上使用变通方法
    公共抽象类基任务
    其中TProc:BaseProcess
    其中TProcParam:BaseParam
    其中TParam:TProcParam
    {
    }
    //通用快捷方式重载
    公共抽象类BaseTask:BaseTask
    其中TProc:BaseProcess
    其中TParam:TProcParam
    {
    }
    
    这样,我可以同时做到:

    public class SomeTask : BaseTask<SomeProcess, SomeParam> { }
    
    public class SuperTask : BaseTask<SomeProcess, SomeParam, SuperParam> { }
    
    公共类SomeTask:BaseTask{}
    公共类超级任务:BaseTask{}
    
    Hi,只是为了清楚起见,但是您也可以发布编译器错误吗?“但是里面的代码可以正常工作”您怎么知道这一点?如果看不到“内部代码”,很难知道这一点。好吧,你尝试了一些东西,但它不起作用,但我想知道你的问题是什么?你的基类在
    TParam
    中不是、不可能是、永远不会是协变的,因为
    参数
    有一个setter。C#也不允许类变(我认为这是一个很大的疏忽),但即使它变了,也不能使它变为协变的。您可以在通用接口和委托中使用out关键字。尝试使用@Arturomenchaai的方法可以,但是编译器不能告诉我
    SomeProcess
    SuperProcess
    是兼容的。仔细想想,我想做的对编译器来说可能没有意义——我想让他检查它是否兼容,但不要太严格……@Toxantron:我认为没有中间类型就无法做到这一点,因为一方面,你需要一个比
    BaseParam
    (只能使用协方差或添加第二个泛型参数),另一方面,参数对处理器有效,但不一定是同一类型(第三个泛型参数)。
    public class SuperTask : BaseTask<SomeProcess, SuperParam> { }
    
    // Use workaround on BaseTask
    public abstract class BaseTask<TProc, TProcParam, TParam>
       where TProc : BaseProcess<TProcParam>
       where TProcParam : BaseParam
       where TParam : TProcParam
    {
    }
    
    // Generic shortcut overload
    public abstract class BaseTask<TProc, TParam> : BaseTask<TProc, TParam, TParam>
       where TProc : BaseProcess<TProcParam>
       where TParam : TProcParam
    {
    }
    
    public class SomeTask : BaseTask<SomeProcess, SomeParam> { }
    
    public class SuperTask : BaseTask<SomeProcess, SomeParam, SuperParam> { }