【问题标题】:C# extended interface implementation as a parameter to delegate that takes base interfaceC# 扩展接口实现作为一个参数来委托,它采用基本接口
【发布时间】:2016-12-19 15:45:25
【问题描述】:

我有一个扩展了其他接口的接口,如下所示:

interface IBase
{
  int Id { get; set; }
  string Name { get; set; }
}

interface IExtended : IBase
{
  bool IsChecked { get; set; }
}

然后我使用基接口作为委托函数中的参数,它也是类构造函数的参数,如下所示:

public class SomeClass
{
  private IBase _model;
  private Func<IBase, string> _handler;

  public SomeClass(IBase model, Func<IBase, string> handler)
  {
    _model = model;
    _handler = handler;
  }

  public string ExecuteHandler()
  {
    return _handler(model);
  }
}

接口实现:

public class BaseImplementation : IBase
{
  int Id { get; set; }
  string Name { get; set; }

  public BaseImplementation(int id, string name)
  {
    Id = id;
    Name = name;
  }
}

public class ExtendedImplementation : IExtended
{
  int Id { get; set; }
  string Name { get; set; }
  bool IsChecked { get; set; }

  public BaseImplementation(int id, string name, bool isChecked)
  {
    Id = id;
    Name = name;
    IsChecked = isChecked;
  }
}

预期用途:

BaseImplemetation baseModel = new BaseImplementation(1, "base");
ExtendedImplemetation extendedModel = new ExtendedImplementation(2, "extended", true);

SomeClass someClass1 = new SomeClass(baseModel, (IBase arg) => {
  Console.Write("Remember, " + arg.name + ", YOLO!");
});

SomeClass someClass2 = new SomeClass(extendedModel, (IExtended arg) => {
  Console.Write(arg.name + ", YOLO! You're " + (arg.IsChecked) ? "checked!" : "not checked!");
});

string res1 = someClass1.ExecuteHandler();
string res2 = someClass2.ExecuteHandler();

但是那 ( 不起作用,即使 IExtended 的实现必然会实现 IBase 接口定义的所有内容。为什么会这样,我将如何绕过它并获得我想要的结果?

编辑:

我想我现在明白了。

我认为 Func&lt;IBase, string&gt; 等于 Func&lt;IExtended, string&gt; 因为 IExtended 当然实现了 IBase 所做的一切,所以应该没有问题,对吧?按照我的意愿和在我的示例中列出的实现当然可以正常工作。

但是!问题是 someClass2 不能这样构造,因为正如@Servy 提到的,委托函数可以做这样的事情:

SomeClass someClassWrong = new SomeClass(baseModel, (IExtended arg) => {
  if (arg.IsChecked) {
    // gotcha, baseModel doesn't have IsChecked property!
  }
});

编辑 2:

感谢大家的帮助,很抱歉不断编辑并给出我想要的错误示例:D

【问题讨论】:

    标签: c# inheritance interface


    【解决方案1】:

    但这不起作用,即使IExtended 的实现必然会实现IBase 接口定义的所有内容。为什么会这样,我将如何绕过它并获得我想要的结果?

    当SomeClass 调用该委托时它实际上可能不会传递IExtended 实例。允许提供 any IBase 实例作为参数,所以如果它提供了一个 不 实现 IExtended 的实例,那么您希望您的代表做什么?

    如果SomeClass总是要传递一个IExtended 实例,然后相应地修改它在其构造函数中接受的委托,以便调用者始终知道他们得到了一个IExtended实例作为参数。

    【讨论】:

    • 委托应始终仅使用 SomeClass 中的 IBase 中定义的方法(因此在此示例中为 IBase.SomeMethod())。 IExtended 中定义的其他方法用于应用程序的其他部分,但在这种特定情况下,我也想将现有委托与 IExtended 实现一起使用。
    • @DekiChan 就像我说的那样,您在调用委托时传递给委托的实际对象始终是 IExtended 实例,在这种情况下,您需要更改您正在接受的委托,或者他们不是,并且提供委托的代码不能假定它们是 IExtended 实例。您不能提供不是IExtended 的IBase 对象并获得IExtended 行为。
    • 我扩展了我的问题,希望能更清楚地解释我想做什么。
    • @DekiChan 在您的示例中,在这两种情况下,您都提供了一个接受 no 参数并返回 string 的方法。如果这就是你想要支持的,那么这就是你的代表签名应该是的。
    • 我发现我还没有解释我真正想做的事情。但我认为首先我需要睡个好觉,因为显然我的大脑目前的功能并不好。但是到目前为止,谢谢你,我明天会回复。
    【解决方案2】:

    您可以简单地定义一个知道IBase 确实是IExtended 的委托:

    SomeClass someClass = new SomeClass((IBase arg) => { (arg as IExtended).DoSomethingOnlyExtendedKnowsAbout(); });
    

    这可能是不安全的,但如果你能以某种方式强制传递给特定 lamda 的 arg 将始终是 IExtended,那么就没有害处。您还可以在 lambda 本身中提供一种安全机制,并在调用堆栈中相应地对其进行管理:

    SomeClass someClass = new SomeClass((IBase arg) => { (arg as IExtended)?.DoSomethingOnlyExtendedKnowsAbout(); });
    

    【讨论】:

    • 如果您总是要传递IExtended,那么您应该只是更改委托签名。
    • @Servy 不知道完整的场景,但情况并非总是如此。也许这个具体的SomeClass 总是会收到IExtended 参数,但IBaseClass 通常不会。 This question 和答案演示了如果构造正确,这将如何是类型安全的。
    • 感谢您的帮助,当我试图使其更清晰时,我问错了问题 - 我再次对其进行了编辑并解释了一切。我想现在很清楚我想要什么,哪里错了:)
    【解决方案3】:

    我没有看到问题。根据您拥有的代码,以下内容按预期工作:

    public class SomeClass
    {
        public SomeClass(Func<IBase, string> handlerFcn)
        {
            // something gets done
            this.Handler=handlerFcn;
        }
        public Func<IBase, string> Handler { get; set; }
    }
    
    public static class Program
    {
        static void Main(string[] args)
        {
            var s1 = new SomeClass((x) => x.SomeMethod());
            var xt = new ExtendedClass();
            var result = s1.Handler(xt);
            // result = "yolo extended edition!"
        }
    }
    

    我认为您试图在 lambda 定义中使用具体类 ExtendedClass,除非您使用 define it as a closure,否则这将不起作用。

    【讨论】:

      猜你喜欢
      • 2021-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-30
      • 2016-04-28
      • 2013-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多