2016-12-19 3 views
1

У меня есть интерфейс, который расширяет некоторый другой интерфейс, например:С в качестве параметра делегат, который принимает базовый интерфейс

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. Почему это так, и как бы я обойти это и получить результат, который я хочу?

EDIT:

Я думаю, что я получил его.

Я думал, что Func<IBase, string> равно Func<IExtended, string> потому IExtended конечно реализует все, что IBase делает, поэтому не должно быть никаких проблем, не так ли? Реализация, поскольку я хотел, чтобы она была и указана в моем примере, конечно, будет работать нормально.

НО! Проблема заключается в том, что someClass2 не может быть построена так, потому что, как уже упоминалось @Servy, функция делегат может сделать что-то вроде этого:

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

EDIT 2:

Спасибо всем вам за помощью и извините за постоянное редактирование и дает неправильный пример SOF, что я хочу: D

ответ

3

Но это не работает, даже если реализация IExtended обязательно реализовать все, что определяется по IBase interfa се. Почему это так, и как бы я обойти это и получить результат, который я хочу?

SomeClass Когда вызывает этот делегат не может фактически передать IExtended экземпляр. Разрешено предоставлять любое значениеIBase как параметр, поэтому, если он содержит то, что не реализовать IExtended, то что бы вы ожидали от своего делегата?

SomeClass Если это всегда собирается передать IExtended экземпляр, а затем изменить делегат он принимает в своем конструкторе, соответственно, так что абоненты всегда знают, что они получают в IExtended экземпляр в качестве параметра.

+0

Делегат должен всегда использовать только методы, определенные в IBase (так в этом примере IBase.SomeMethod()) внутри SomeClass. Другие методы, определенные в IExtended, используются в других частях приложения, но в этом конкретном случае я хотел бы использовать существующий делегат с реализациями IExtended. – DekiChan

+0

@DekiChan Как я уже сказал, либо фактические объекты, которые вы передаете делегату при его вызове, всегда являются экземплярами «IExtended», и в этом случае вам нужно изменить делегат, который вы принимаете, или нет, и код предоставляя делегату, не может предположить, что они являются экземплярами «IExtended». Вы не можете предоставить объекты 'IBase', которые не являются' IExtended', и получить поведение 'IExtended'. – Servy

+0

Я расширил свой вопрос, надеюсь, более ясное объяснение того, что я хотел бы сделать. – DekiChan

0

Вы можете просто определить делегат, который знает в IBase действительно является IExtended:

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

Это потенциально небезопасно, но если вы каким-то образом можно принудительно, что arg прошел к этому конкретному LAMDA всегда будет IExtended, тогда нет никакого вреда. Вы также можете обеспечить механизм безопасности в самой лямбда и управлять им соответственно в стеке вызовов:

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

Если вы * всегда * собираетесь проходить в 'IExtended', вы должны просто изменить подпись делегата. – Servy

+0

@Servy Не зная полного сценария, это не всегда так. Возможно, этот конкретный 'SomeClass' всегда будет принимать аргументы IExtended, но' IBaseClass' обычно не будет. [Этот вопрос] (http://stackoverflow.com/a/39821458/767890) и ответ показывает, как это может быть безопасным по типу, если оно построено правильно. – InBetween

+0

Благодарим вас за помощь, я задал неправильный вопрос, когда попытался прояснить ситуацию - я снова отредактировал и объяснил все. Думаю, теперь понятно, что я хотел и что было не так :) – DekiChan

0

Я не вижу проблемы. На основании кода у вас есть следующие работы по их назначению:

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

Я думаю, что вы пытаетесь использовать конкретный класс ExtendedClass в определении лямбды, и что не будет работать, если вы define it as a closure.

Смежные вопросы