1

I think my question is best descirbed by a code snippet:

class A
{
    public void FunctionToBeCalled();
}
class B
{
    public void FunctionToBeCalledAfter();
}

Now, after a FunctionToBeCalledAfter() call, FunctionToBeCalled() needs to "know" it must be called. B cannot have an A member, but A can have a B member. Is there any way this can be implemented in C#?

Why i need this: Class A is Application level on OSI stack. Classes B and C(unmentioned before) are Transport Level. C makes calls to FunctionToBeCalledAfter, and after this FunctionToBeCalled needs to be called. But sincer A is a higher level, B and C cannot depend(have a member A), i don't know how to call FunctionToBeCalled.

12
  • 2
    Can you clarify what you mean by "FunctionToBeCalled() needs to "know" it must be called."? Commented Nov 23, 2021 at 10:49
  • 3
    "B cannot have an A member, but A can have a B member" -> Why? Your example seems to have been oversimplified here. Commented Nov 23, 2021 at 10:51
  • 2
    You're going to have to tell me what's wrong with b.FunctionToBeCalledAfter(); a.FunctionToBeCalled(); Commented Nov 23, 2021 at 10:51
  • He wants to call a.FunctionToBeCalled().FunctionToBeCalledAfter() instead off b.FunctionToBeCalledAfter(); a.FunctionToBeCalled(); Commented Nov 23, 2021 at 10:53
  • I have only one instance of class A in my program, but multiple B instances. Whenever a B instance calls FunctionToBeCalledAfter(), the A instance needs FunctionToBeCalled(). I don't know how to do this(or if it's possible at all) without having an A instance as a member of class B(which is a necessity). @Llama Commented Nov 23, 2021 at 10:54

2 Answers 2

4

I see 2 ways to accomplish this, one easier but (arguably) less elegant, one a little more involved but (arguably) more elegant

  1. The less elegant solution: Singleton

A Singleton pattern enforces that there can only ever be one instance of a class at any given time, this seems to line up with your description of A (which from here on out I'll call Foo, and I'll be calling B Bar). So let's implement it:

public class Foo
{
    private static Foo _instance;

    public static Foo Instance => _instance ?? (_instance = new Foo());

    // Private constructor so no one else can instantiate Foo
    private Foo() { }

    public void FunctionToBeCalled() { /* your code here */ }
}

public class Bar
{
    public void FunctionToBeCalledAfter()
    {
        // Your existing code here
        Foo.Instance.FunctionToBeCalled();
    }
}

Now, the problem here is if your requirements ever change and you need multiple Foos, that'll be quite a refactor to implement it. Another (larger) downside is that we explicitly reference (i.e depend on) Foo, which isn't great and a problem if Bar is inside a project/ library that cannot directly reference Foo. Luckily solution 2 fixes those problems:

  1. The more elegant solution: Events
public class Foo
{
    // We don't need Foo to be a singleton anymore
    public void FunctionToBeCalled() { /* Your code here */ }
}

public class Bar
{
    public delegate void FunctionToBeCalledAfterEventHandler();

    public event FunctionToBecalledAfterEventHandler FunctionToBeCalledAfterEvent;

    public void FunctionToBeCalledAfter()
    {
        // Your existing code here
        OnFunctionToBeCalledAfterEvent(); // Fire the event
    }

    private void OnFunctionToBeCalledAfterEvent()
    {
        FunctionToBeCalledEvent?.Invoke();
    }
}

Now, everywhere where you're creating an instance of Bar you need to have a reference to Foo and subscribe to the event like so:

// foo = instance of class Foo
var bar = new Bar();

// The compiler is smart enough to find out that 'FunctionToBeCalledAfterEvent'
// has the same signature as 'FunctionToBeCalledAfterEvent' and can call it directly
// If this just so happens to not be case, see second way to subscribe to events
bar.FunctionToBeCalledAfterEvent += foo.FunctionToBeCalled;

// Or
bar.FunctionToBeCalledAfterEvent += () => foo.FunctionToBeCalled();

Events are great

Sign up to request clarification or add additional context in comments.

Comments

1

Class B can have an event that other parties can handle. At the end of B.FunctionToBeCalledAfter this event would be invoked. Anyone who registered for this event would then be notified. Usual boilerplate code involves one virtual method that invokes one event. It's the standard way of adding events. If there is no need for additional data in the event then EventArgs is used. If additional data is needed then you could replace EventArgs with EventArgs<YourData>, or as an alternative, introduce a class XxxArgs derived from EventArgs with this additional data.

Class B
{
    public event EventHandler FinishedFunctionToBeCalledAfter;

    protected virtual void OnFinishedFunctionToBeCalledAfter(EventArgs e)
    {
        EventHandler handler = FinishedFunctionToBeCalledAfter;
        handler?.Invoke(this, e);
    }

    public void FunctionToBeCalledAfter()
    {
        ...

        OnFinishedFunctionToBeCalledAfter(EventArgs.Empty);
    }
}

Now when class A gets a hold of an object of class B it would add its event handler to it:

class A
{
    public void FunctionToBeCalled();

    public void FinishedFunctionToBeCalledAfter(object source, EventArgs e);

    public void IntroduceObject(B b)
    {
        b.FinishedFunctionToBeCalledAfter += FinishedFunctionToBeCalledAfter;
    }
}

When this object b of class B should end its life class A must know about it so that it can remove its event handler:

b.FinishedFunctionToBeCalledAfter -= FinishedFunctionToBeCalledAfter;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.