简体   繁体   中英

Delegate with base and/or inherited class parameter

I've got a system i'm working with that uses System.Action delegates to handle events, but have run into an issue with incompatible delegate types and am unsure the course of action to take. The below code is a close approximation of what's going on in the source.

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

The delegate assignment passes the compiler fine but fails at run-time for RegisterBasic() as it's unable to resolve the child parameter back to it's parent. I've got a feeling i really need to refactor to add, and better take into account, an OnDoesBasicStuff and an OnDoesMoreComplexStuff callback each.

Stumbled upon a blog that identified a solution that works. https://faithlife.codes/blog/2008/07/casting_delegates/

Excerpt from blog: "Fortunately, it is possible to cast between arbitrary delegate types, though it isn't as efficient as you might like."

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

Using their utility class, i was able to cast the delegate type to it's base class which worked.

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

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