Делегировать с базовым и / или унаследованным параметром класса - PullRequest
0 голосов
/ 17 января 2020

У меня есть система, с которой я работаю, которая использует делегаты System.Action для обработки событий, но столкнулась с проблемой с несовместимыми типами делегатов и не уверена, какие действия предпринять. Приведенный ниже код является близким приближением к тому, что происходит в исходном коде.

Using System;

//My base data object
public class BasicData{
    public bool foo {get; private set;} = false;

    public BasicData(bool bData){
        foo = bData;
    }
}

//One of my extended data objects
public class ComplexData : BasicData{
    public string bar {get; private set;} = "";

    public ComplexData (bool bData, string strData){
        foo = bData;
        bar = strData
    }
}

//One of the basic things doing work
public class MyThing{
    public bool isAwake {get; private set;} = false;
    public Action<BasicData> OnDoesStuff;
    private BasicData data; //Used internally elsewhere

    protected virtual void DoThatStuff() {
        data = new BasicData(true);
        OnDoesStuff?.Invoke(data);
    }
}

//One of the more complex things doing work
public class MyMoreComplexThing : MyThing{
    public new Action<ComplexData> OnDoesStuff;
    private new ComplexData data; //Used internally elsewhere

    protected override void DoThatStuff() {
        data = new ComplexData(true, "MMM, Tasty");
        OnDoesStuff?.Invoke(data);
    }
}

//Excerpt from one of the 'factory-like' managers
public MyFactory{
    private List<MyThing> lstThings = new List<MyThing>();

    //Generic basic data callback
    //Other interested parties add their own data callbacks as needed
    public void RegisterBasic(BasicData callback) {
        for (int i = 0; i < lstThings.Count; i++) {
            if(lstThings[i].isAwake){
                lstThings[i].OnDoesStuff += callback;
            }
        }
    }
}

//Excerpt from one of the interested parties
public InterestedParty{
    //populated somewhere else during initialization of the party
    private List<MyMoreComplexThing > lstComplexThings = new List<MyMoreComplexThing >();

    //Complex data callback
    public void WhatchaDoing(MyMoreComplexThing callback) {
        for (int i = 0; i < lstComplexThings .Count; i++) {
            if(lstComplexThings [i].isAwake){
                lstComplexThings [i].OnDoesStuff += callback;
            }
        }
    }
}

Назначение делегата проходит компилятор нормально, но не выполняется во время выполнения для RegisterBasi c (), так как он не может разрешить дочерний параметр обратно к его родителю. У меня такое чувство, что мне действительно нужно реорганизовать рефакторинг для добавления, и лучше принять во внимание обратный вызов OnDoesBasicStuff и OnDoesMoreComplexStuff каждый.

1 Ответ

0 голосов
/ 17 января 2020

Наткнулся на блог, который определил решение, которое работает. https://faithlife.codes/blog/2008/07/casting_delegates/

Выдержка из блога: «К счастью, существует возможность выполнять кастинг между произвольными типами делегатов, хотя это не так эффективно, как хотелось бы.»

public static class DelegateUtility {
    public static T Cast<T>(Delegate source) where T : class {
        return Cast(source, typeof(T)) as T;
    }
    public static Delegate Cast(Delegate source, Type type) {
        if (source == null)
            return null;
        Delegate[] delegates = source.GetInvocationList();
        if (delegates.Length == 1)
            return Delegate.CreateDelegate(type,
                delegates[0].Target, delegates[0].Method);
        Delegate[] delegatesDest = new Delegate[delegates.Length];
        for (int nDelegate = 0; nDelegate < delegates.Length; nDelegate++)
            delegatesDest[nDelegate] = Delegate.CreateDelegate(type,
                delegates[nDelegate].Target, delegates[nDelegate].Method);
        return Delegate.Combine(delegatesDest);
    }
}

Используя их служебный класс, я смог привести тип делегата к его базовому классу, который работал.

lstComplexThings [i].OnDoesStuff += DelegateUtility.Cast<Action<MyMoreComplexThing>>(callback);
...