【问题标题】:C# Calling Methods in Generic Classes泛型类中的 C# 调用方法
【发布时间】:2010-03-14 20:14:41
【问题描述】:

我正在从 EmguCV 扩展 ImageBox 控件。控件的Image 属性可以设置为实现IImage 接口的任何内容。

以下所有的都实现了这个接口:

Image<Bgr, Byte>
Image<Ycc, Byte>
Image<Hsv, Byte>

现在我想在上述类型的对象上调用Draw 方法(无论它可能是什么)。

问题是当我访问 Image 属性时,返回类型是IImage。 IImage 没有实现Draw 方法,但以上都实现了。

我相信我可以将IImage 类型的对象转换为上述对象之一(右侧),并且可以访问Draw 方法。但是我怎么知道什么是正确的呢?如果您有更好的方法,也请提出建议。

编辑

抱歉,我忘了提一条重要信息。上述每个类的 Draw 方法都采用一个不同的参数。例如Draw 用于 Image&lt;Bgr, Byte&gt; 采用 Bgr 类型的参数,而 Draw 用于 Image&lt;Hsv, Byte&gt; 采用 Hsv 类型的参数。

【问题讨论】:

  • 是否有权访问 IImage 和 Image 代码?
  • EmguCV 是一个开源项目,虽然我真的更喜欢只使用他们的二进制文件 :)
  • 图片界面/实现看起来设计得不太好。
  • 感谢所有回答的人。我学到了一些非常好的东西。接受的答案通常可能导致非常草率的代码。但就我而言,我只需要做一次,我不想编辑 EmguCV 源文件。我用过这个,所以我把它标记为接受。

标签: c# .net generics casting


【解决方案1】:

添加一个继承自 IImage 的 IDrawableImage。

public interface IDrawableImage : IImage
{
   void Draw(object val);
   void Draw();
}

然后您可以简单地执行以下操作:

var drawableImage = container.Image as IDrawableImage;
if (drawableImage != null)
    drawableImage.Draw();

为了符合您上面的说明:

public interface IDrawableImage<T, B> : IDrawableImage where B : byte
{
    void Draw<T>(T val);
}

那么如果你知道类型:

var value = new Hvr();
var drawableImage = container.Image as IDrawableImage<Hvr, Byte>;
if (drawableImage != null)
    drawableImage.Draw(value);

如果你不这样做

var value = new Hvr();
var drawableImage = container.Image as IDrawableImage;
if (drawableImage != null)
    drawableImage.Draw(value);

【讨论】:

  • 为此类问题创建接口是最佳实践。检查每个实现(如在 Adel 的示例中)违反了打开/关闭原则 (en.wikipedia.org/wiki/Open/closed_principle) 并创建了高耦合 (en.wikipedia.org/wiki/Coupling_%28computer_science%29)。使用此接口,您可以创建一个新的Image&lt;T, Byte&gt; 类型并使用它,而无需更改任何调用Draw 方法的代码。
  • 这正是我建议这种方法的原因,多年来,做大的 if 语句让我的后脑勺比我数不清的多,尤其是当你开始到处这样做的时候。突然之间,您需要一篇关于如何向其添加新类型的内部 wiki 文章,因为它涉及大量代码。
【解决方案2】:

您应该有一个添加 Draw() 的共享接口。

【讨论】:

    【解决方案3】:

    如果我理解正确的话——Image 类实现了 draw 方法。我不知道你打算如何使用它,但你可以这样做:

    private void DrawImage<T1, T2>(Image<T1, T2> image)
    {
        image.Draw();
    }
    

    上面的主要问题是上面方法的调用者应该指定T1和T2类型。

    如果您无法控制 IImage 接口及其实现,那么我认为您可以从两种解决方案中进行选择:指定 T1 和 T2 类型,或者让 ifs/switches 具有所有可能的 IImage 实现。

    如果您可以访问 IImage 接口和 Image 类,那么您应该添加通用 IImage 接口并为其添加 Draw 方法。

    【讨论】:

    • 这样你就需要针对不同的泛型类型使用不同的方法。
    • 为什么?您只需使用必要的类型参数调用上述方法。
    【解决方案4】:

    这样的事情怎么样:

    void foo( IImage image )
    {
        if ( image is Image<Bgr, Byte> )
        {
            ((Image<Bgr, Byte>)(image)).Draw();
        }
    
        // handle the other types the same way.
    }
    

    【讨论】:

    • 我认为最好有一个实现而不是多个 if。
    • 这只是乞求草率的代码。现在,对于每种类型,您都必须在多个位置添加代码。将这个逻辑以及不同图像类型的所有逻辑外推到某种渲染器中可能比做这样的事情更好。
    • 我同意还有其他好的答案,这通常会导致代码非常草率。但就我而言,我只需要做一次,我不想编辑 EmguCV 源文件。我使用了这个,所以我会将它标记为已接受。
    【解决方案5】:

    如果您确定您将拥有的 Image(无论类型如何)将具有 Draw 方法,您可以使用反射并绕过它:

    我会把它作为一种扩展方法来做:

    public static void Draw(this IImage image)
    {
       var method = image.GetType().GetMethod("Draw");
       if(method != null)
       {
          method.Invoke(image,null);
       }
    }
    

    有点骇人听闻,但话又说回来,我会说 API 设计不正确。至少应该有一些 Drawer 类或知道如何作用于图像的东西。

    【讨论】:

    • 不,反射很慢,建议的解决方案不是使用反射的自然方式。
    • 如果你从 MethodInfo 创建一个委托并缓存它而不是每次都查找它,重复调用它根本不会慢。
    【解决方案6】:

    您的问题缺少关键信息:您要编写的代码。

    我们需要知道您打算如何编写调用Draw(...) 方法的代码。

    【讨论】:

      【解决方案7】:

      或者,您可以使用动态方法并抓住机会。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-04-23
        • 1970-01-01
        • 2012-04-21
        • 1970-01-01
        • 2012-01-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多