C#泛型:无法从“具体类”转换为“接口”错误

Aar*_*ray 5 c# generics casting

尝试生成要应用于图像的过滤器的通用队列(在示例过滤器中使用的地方OpenCVSharp.GaussianBlur,但使其成为通用的,以便我可以插入我创建的任何自定义过滤器)。

我在C#泛型方面有些挣扎,而intellisense显示:

无法从“ GaussianBlur”转换为“ IFilter”

Intellisense建议更改以下行:

filters.Enqueue(filter);

通过投射到界面

filters.Enqueue((IFilter<IFilterParams>)filter);

但是,我的问题是,当具体类实现接口并由泛型定义要求时,为什么需要强制转换?或者我误解了如何使用泛型声明类。

当前的实现代码如下:

public class FilterTest
{
    private FilterCollection filters = new FilterCollection();

    /* ... other irrelevant code ... */

    public void ApplyFilters(ref Mat buffer)
    {

        var filter = new GaussianBlur(new GaussianBlurParams { KernelSize = new Size(6, 6) });
        filters.Enqueue((IFilter<IFilterParams>)filter);
        filters.Apply(ref buffer);

    }
}
Run Code Online (Sandbox Code Playgroud)

。我正在为FilterCollection扩展Queue <>类:

public class FilterCollection : Queue<IFilter<IFilterParams>>
{

    public void Apply(ref Mat buffer)
    {
        while (Count > 0)
            Dequeue().Apply(ref buffer);
    }

}
Run Code Online (Sandbox Code Playgroud)

IFilter和IFilterParams的接口如下:

public interface IFilter<T> where T : IFilterParams
{
    void Apply(ref Mat buffer);
}

public interface IFilterParams { }
Run Code Online (Sandbox Code Playgroud)

然后是示例过滤器实现(在这种情况下,或多或少只是一个包装器):

public class GaussianBlurParams : IFilterParams
{
    public Size KernelSize = new Size(5, 5);
    public double SigmaX = default(double);
    public double SigmaY = default(double);
    public BorderTypes BorderType = BorderTypes.Default;
}

public class GaussianBlur : IFilter<GaussianBlurParams>
{
    private GaussianBlurParams p;

    public GaussianBlur(GaussianBlurParams filterParams)
    {
        this.p = filterParams;
    }

    public void Apply(ref Mat buffer)
    {
        Cv2.GaussianBlur(buffer, buffer, p.KernelSize, p.SigmaX, p.SigmaY, p.BorderType);
    }
}
Run Code Online (Sandbox Code Playgroud)

因此给出:

  • GaussianBlur 实施 IFilter<GaussianBlurParams>
  • IFilter<T> where T : IFilterParams
  • GaussianBlurParams实施IFilterParams

是使用强制转换解决此问题的唯一方法,还是所编写的通用类/接口的结构有问题?

Sef*_*efe 4

您的代码有多个方面在这里纠缠在一起,这使得设计不太理想。乍一看,这可能看起来像是协方差问题,但仔细一看,情况并非如此。这里的两个主要方面是通用约束接口。为了理解我的意思,让我们看一下这两种语言元素的一些好处。

通用约束

虽然泛型使您能够以类型安全的方式使用多种类型的模式实现,但您无法做太多事情来直接操作T泛型类中的类型对象。您不能创建实例,不能依赖引用或值类型的实例(尝试与 进行比较null以了解这意味着什么),并且不能访问 中定义的成员之外的任何其他成员System.Object。这就是为什么您可以使用泛型约束来允许泛型类中的代码可以对 type 的对象执行其他操作T,例如创建实例(使用约束new())或访问其他成员(通过限制T为某种类型和/或一个或多个接口) )。

接口

接口提供了合同保证,实现者将拥有一组定义的成员。这种保证是针对接口的使用者的,而不是针对其实现者的。这意味着您不会使用接口来强制其实现者提供一些对接口的使用者没有任何价值的成员。

这对你的情况意味着什么

问题的关键是代码的这一部分:

public interface IFilter<T> where T : IFilterParams
{
    void Apply(ref Mat buffer);
}

public interface IFilterParams { }
Run Code Online (Sandbox Code Playgroud)

尤其:

  1. 您定义通用约束where T : IFilterParams,但IFilterParams不提供任何成员。此约束不会为您的设计增加任何价值。您将实现者限制为某个特定的T,但您不会从中获得任何东西,因为您无法对T没有限制就无法执行的实例执行任何操作。

  2. 更进一步,您根本不需要接口是通用的。您甚至不使用T接口提供的唯一成员。就接口的保证而言,没有它你也可以做得很好。

  3. 看一下 的GaussianBlur实现IFilter<T>,很明显您GaussianBlurParams在构造函数中使用了 only ,它不是接口的一部分。因此,您使用接口的约束只是为了限制实现者使用Params实现IFilterParams. 这甚至不是真正的限制,因为实现者可以使用任何其他参数类进行初始化。但这主要违反了接口为其使用者提供保证而不是限制其实现者的原则。

把这一切放在一起,你可以简单地去......

public interface IFilter
{
    void Apply(ref Mat buffer);
}
Run Code Online (Sandbox Code Playgroud)

...并且您已经避免了所面临的所有问题。

即使您需要接口的另一个T使用者的约束(也许您的示例中还没有添加另一个接口成员),您也不需要此约束。因此,您仍然可以保留非泛型并提供另一个提供附加功能的接口(可能会或可能不会继承自 )。where T : IFilterParamsFilterCollectionIFilterIFilter