【问题标题】:C# Calling internal methods of an object passed as interfaceC#调用作为接口传递的对象的内部方法
【发布时间】:2015-01-12 15:32:10
【问题描述】:

我有一个接口 IFoo 定义了一些函数,一个抽象类 FooBase 实现了 IFoo 和一些内部函数,以及几个从 FooBase 派生的类 Foo。
我还有一个类 Bar,它应该从 FooBase 调用一个方法,但它的参数作为 IFoo 传递。所以一切看起来像这样:

public interface IFoo
{
  // Some methods
}

public abstract class FooBase : IFoo
{
  // Methods from IFoo

  internal TInternalType SomeMethod();
}

public class Foo1 : FooBase
{
  // ...
}

public class Bar
{
  public void DoSomething(IFoo foo)
  {
    // This does not feel right:
    TInternalType myT = (foo as FooBase).SomeMethod();
  }
}

如前所述,这感觉不对,因为任何人都可以来,编写一个实现 IFoo 的 Baz 类并用它调用 DoSomething 将失败。

我还找到了this 问题/解决方案,以解决有关具有公共接口的内部内容的类似问题,但这不会改变某些第三方类实现 IFoo 会导致问题的问题,即我仍然有一个撒谎的 API。

另外,我可以更改 DoSomething 的类型以获取 FooBase 类型的参数并通过将构造函数设为内部来从第三方类中保护 FooBase,但我希望在我的公共 API 中只包含抽象,而不是实现。

简而言之,我要问:图书馆有没有办法只公开抽象(即图书馆的用户只使用这些抽象)但仍然访问内部成员?

背景:
我正在编写一个公开某些类型并使用另一个库 L 来实现其功能的库。使用 L 的事实是我想对我的图书馆用户隐藏的实现细节,所以当出于某种原因我必须用其他东西替换 L 时,我的用户不会注意到。

我现在有一些将向我的用户公开的 Foo 类,以及一个将与 L 一起使用的 Bar 类。对于每个 Foo 类,必须创建 L 中一个类的相应实例;但是单独的 Foo 类太不同了,它们每个都需要单独的逻辑来创建各自的 L 类。

这就是为什么我在 FooBase 中定义了一个内部抽象方法,并且每个 Foo 都实现了自己的方法来从 L 生成其类,因此 Bar 可以调用这个方法来获取它需要与 L 一起使用的东西。

也许有一种与内部重载方法完全不同的方法。

【问题讨论】:

  • 如果你依赖 FooBase 的实现,你应该使用它作为你的方法参数。
  • 如果你真的想使用反射,但请接受上述评论。
  • 如果您唯一担心的是 DoSomething 会因为任何实现 IFoo 而不是从 FooBase 继承的东西而失败,那么为什么不呢:var probablyFooBase = foo as FooBase; if (probablyFooBase != null) { probablyFooBase.SomeMethod(); }
  • "i want to have only abstractions in my public API, not implementations." - 好吧,FooBaseabstract class,所以您的条件仍然满足。
  • InternalsVisibleToAttribute 是否适用于您的情况(需要强签名的程序集)?

标签: c# interface


【解决方案1】:

如果您想避免向用户公开您的基类,为什么不创建另一个派生自IFoo 的接口,将其称为IFooWithSomeMethod 之类的名称,为其添加SomeMethod 方法,并在@ 中实现该接口987654324@?那么Bar.DoSomething 可以使用IFooWithSomeMethod 而不是IFoo

顺便说一句,很抱歉在第一遍中搞砸了一些名字。希望我现在已经把这一切都理顺了。

【讨论】:

  • 哎呀,我搞砸了。 SomeMethod 应该是 internal 的原因是,它返回一个内部类型。我编辑了我的问题。
  • 那么如何从内部类型创建一个表达您想要的功能的接口,从该接口派生内部类型,并从SomeMethod返回该接口的一个实例?
  • @C.Sura - 重要信息。但是,如果您唯一担心的是 (foo as FooBase).SomeMethod(); 可能会引发 NullReference 异常,那么为什么不先检查 if (foo is FooBase) 呢?还是有其他顾虑?
  • 另一个问题是当它不是从 FooBase 派生时没有默认行为,即 DoSomething 不可用;那么我无论如何都必须抛出异常。 - 我实际上在这里依赖 FooBase,但我不想在 API 中依赖这种类型,因为它已经是一个实现而不是抽象。但是如果没有别的办法,那我想我得把参数类型改成FooBase了。
【解决方案2】:

接口的全部意义在于展示实现的能力。如果您需要访问实现的内部成员,您应该让代码的使用者知道他们不是真正的内部成员(通过遵循此处的其他答案,或者例如使用 Interface Segregation 并仅传递个人 role interfaces) ,或者将它们真正保留在内部。在您的情况下,我认为它应该封装在 FooBase 类中。

我想DoSomething 方法也对IFoo 实现做了其他事情。无论Bar 对从内部方法返回的内部类型做什么,都应该在FooBase 中的其他IFoo 方法之一中发生。这里的指导原则是Tell, don't ask

【讨论】:

    【解决方案3】:

    如果FooBase 是一个特殊情况(所以你不想要decalare IFooWithDoSomething)需要额外的调用,你可以实现一个扩展到@ 987654323@,类似这样的:

    public static class FooExtensions {
      public static void DoSomething(this IFoo value) {
        FooBase special = value as FooBase;
    
        if (null != special)
          special.SomeMethod();
      }
    }
    
    public class Bar
    {
      public void DoSomething(IFoo foo)
      {
        // Extension is called
        foo.SomeMethod();
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-23
      • 2015-07-01
      • 2013-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多