简体   繁体   中英

C# extended interface implementation as a parameter to delegate that takes base interface

I have an interface that extends some other interface, like this:

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

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

Then I use base interface as a parameter in a delegate function that is also a parameter to class constructor, like this:

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);
  }
}

Interface implementations:

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;
  }
}

Intended use:

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();

But that ( doesn't work, even though implementation of IExtended would necessarily implement everything that is defined by IBase interface. Why is that so and how would I bypass this and get the result I want?

EDIT:

I think I got it now.

I thought that Func<IBase, string> is equal to Func<IExtended, string> because IExtended of course implements everything that IBase does, so there should be no problem, right? Implementation as I wanted it to be and is listed in my example would of course work just fine.

BUT! The problem is that someClass2 can't be constructed like that because, as @Servy mentioned, delegate function could do something like this:

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

EDIT 2:

Thank you everybody for you help and sorry for constant editing and giving wrong example sof what I want :D

But that doesn't work, even though implementation of IExtended would necessarily implement everything that is defined by IBase interface. Why is that so and how would I bypass this and get the result I want?

When SomeClass invokes that delegate it might not actually pass an IExtended instance . It's allowed to provide any IBase instance as the parameter, so if it provides one that doesn't implement IExtended , then what would you expect your delegate to do?

If SomeClass is always going to pass an IExtended instance, then modify the delegate it accepts in its constructor accordingly, so that the callers always know they're getting an IExtended instance as a parameter.

You can simply define a delegate that knows the IBase is really an IExtended :

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

This is potentially unsafe, but if you somehow can enforce that the arg passed to that specific lamda will always be an IExtended then there is no harm. You could also provide a safety mechanism in the lambda itself and manage it accordingly up the call stack:

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

I don't see the problem. Based on the code you have, the following works as intended:

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!"
    }
}

I think you were trying to use the concrete class ExtendedClass in the lambda definition and that won't work unless you define it as a closure .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM