首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C#泛型:无法从“具体类”转换为“接口”错误

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

Stack Overflow用户
提问于 2018-04-13 18:23:09
回答 2查看 4.3K关注 0票数 5

尝试生成一个要应用于图像的通用过滤器队列(在示例过滤器中,它使用OpenCVSharp.GaussianBlur,但使其通用,这样我就可以插件我创建的任何自定义过滤器)。

我正为C#泛型和intellisense所展示的一些东西而挣扎:

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

Intellisense建议更改以下行:

filters.Enqueue(filter);

通过将其转换到接口

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

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

目前的实施守则如下:

代码语言:javascript
运行
复制
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);

    }
}

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

代码语言:javascript
运行
复制
public class FilterCollection : Queue<IFilter<IFilterParams>>
{

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

}

IFilter和IFilterParams的接口如下:

代码语言:javascript
运行
复制
public interface IFilter<T> where T : IFilterParams
{
    void Apply(ref Mat buffer);
}

public interface IFilterParams { }

然后是示例过滤器实现(在本例中或多或少只是一个包装器):

代码语言:javascript
运行
复制
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);
    }
}

因此:

  • GaussianBlur实现IFilter<GaussianBlurParams>
  • IFilter<T> where T : IFilterParams
  • GaussianBlurParams实现了IFilterParams

转换是修复此问题的唯一方法,还是编写的泛型类/接口的结构有问题?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-04-13 20:04:56

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

泛型约束

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

接口

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

这在你的例子中意味着什么

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

代码语言:javascript
运行
复制
public interface IFilter<T> where T : IFilterParams
{
    void Apply(ref Mat buffer);
}

public interface IFilterParams { }

特别是:

  1. 您定义了泛型约束where T : IFilterParams,但是IFilterParams没有提供任何成员。此约束没有为您的设计增加任何价值。您将实现者限制在特定的T上,但是您无法从它中获得任何东西,因为您不能对T的实例执行任何没有约束的操作。
  2. 更进一步,您根本不需要这个接口是通用的。您甚至没有在接口提供的唯一成员中使用T。就接口的保证而言,没有它也可以做得很好。
  3. 看一看IFilter<T>IFilter<T>实现,很明显,您只在构造函数中使用GaussianBlurParams,而构造函数不是接口的一部分。因此,您只使用接口的约束来限制实现者使用实现IFilterParamsIFilterParams类。这甚至不是真正的限制,因为实现者可以使用任何其他参数类进行初始化。但这主要违反了接口为其使用者提供保证的原则,而不是对其实施者的限制。

把这一切加在一起,你可以直接去.

代码语言:javascript
运行
复制
public interface IFilter
{
    void Apply(ref Mat buffer);
}

...and你已经避免了你所面临的所有问题。

即使您需要为接口的另一个使用者提供带有约束where T : IFilterParamswhere T : IFilterParams(可能在您的示例中还没有添加另一个接口成员),FilterCollection也不需要这个约束。因此,您仍然可以保留一个非泛型的IFilter,并提供另一个提供附加功能的接口(该接口可能继承也可能不继承IFilter)。

票数 4
EN

Stack Overflow用户

发布于 2018-04-13 19:45:41

好的,感谢@zzxyz最初的注释以及添加的注释和答案,然后在删除后不久,引导我对协方差进行更多的研究(我通过添加泛型IFilterParams以避免协方差来创建协方差),SO (Contravariance? Covariance? What's wrong with this generic architecture...?)中的答案/注释帮助我纠正问题并更好地构造代码。

现在我明白了,当我需要把香蕉加入碗(香蕉)的时候,我是如何尝试‘把香蕉加到碗里(水果)(水果是协变的,因为它不仅仅是’一种‘水果’的。我理解,但很难总结出一个不幸被删除的答案。

在研究中,我能够通过为filterParams创建一个具有自己的泛型类型的抽象类并完全删除IFilterParams接口来消除协方差,因此所有过滤器都必须实现基类抽象类,现在不再导致协方差。

由于我现在理解它,但还不足以清楚地解释(上面),修改后的代码(下面)可能有助于更好地解释。

首先,不需要对FilterTest类进行任何更改(除了从最初的示例中删除转换(这是问题的重点):

代码语言:javascript
运行
复制
public class FilterTest
{
    private FilterCollection filters = new FilterCollection();

    public void ApplyFilters(ref Mat buffer)
    {

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

    }
}

接下来,调整队列,使其不是公开所需的'Apply‘方法的协变量(实现一个'type’IFilter)。

代码语言:javascript
运行
复制
public class FilterCollection : Queue<IFilter>
{

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

}

public interface IFilter
{
    void Apply(ref Mat buffer);
}

并最终删除了IFilterParams,因为它们不再与原因相关。现在,示例过滤器实现如下所示:

代码语言:javascript
运行
复制
public class GaussianBlur : IFilter
{
    private GaussianBlurParams p;

    public GaussianBlur(GaussianBlurParams filterParams)
        : base(filterParams)
    {

    }

    public override void Apply(ref Mat buffer)
    {
        Cv2.GaussianBlur(buffer, buffer, p.KernelSize, p.SigmaX, p.SigmaY, p.BorderType);
    }
}

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

问题解决了,希望能帮助别人!

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49823085

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档