1

I have a simple interface called IEvent and it just contains this one method:

void Execute();

I have several derived classes from this interface and one of them needs access to an object that the caller of the method owns. The object is used in this fashion:

using (MyObject object = new MyObject(this.MessageService)
{
    foreach (IEvent myEvent in eventList)
    {
        myEvent.Execute(); // <--- I need to pass object without adding argument here if possible?
    }
}

I would add the object as a field in the derived class that needs access to it, but by the time I get to this part of the code, the IEvent objects are already constructed and running on a background thread. Currently, the only way I can think of is to add a setter in the IEvent interface for this object, but then I am exposing a field that most derived classes won't care about and doesn't seem like a clean solution.

I would add it as an argument to Execute(), but the problem is that the object belongs to an assembly that the assembly that contains IEvent doesn't know about (and don't want it to know about) and again 99% of the events don't care about this object anyway. Is there a better way to accomplish what I am trying to do here?

Andrew
  • 1,581
  • 3
  • 18
  • 31
  • Make an _override_ –  Mar 17 '16 at 03:38
  • How can the override contain an argument while the base method doesn't? – Andrew Mar 17 '16 at 03:40
  • What about passing an optional parameter in Execute()? – Thakur Mar 17 '16 at 03:49
  • 1
    I think optional parameter is the easiest approach to this without a lot of modifications and repercussions. – h-rai Mar 17 '16 at 03:56
  • That's unfortunate. I might as well use the setter approach I described then. Both don't seem like clean solutions. – Andrew Mar 17 '16 at 04:12
  • "How can the override contain an argument while the base method doesn't?" Have you tried adding virtual to the Method? – Aizen Mar 17 '16 at 05:08
  • _"How can the override contain an argument while the base method doesn't? "_ - obviously by adding it to the interface. Or you can define a `IEvent2` that takes an extra parameter. Your calling code can then query whether the object supports `IEvent2` or not and call the appropriate version –  Mar 17 '16 at 06:34

2 Answers2

0

I'm going to piggyback on Jon Skeet's amazing knowledge of C#, .NET, CLR, IL and everything that surrounds any of those topics. You can't get to the instance of the calling object and especially the local varaible in the calling method. You can get its type, you can get the calling method through StackTrace, for example (StackTrace.GetFrames()), but none of those are going to do you any good in this situation. What you're trying to accomplish would require some heavy dive into the debugging API. As far as walking the stack, here's a quick sample I created to try see if I can figure something out for you (I made assumptions in regards to how your program is structured... obviously it's not a one to one sample):

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace SampleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var eventList = new List<IEvent> { new SomeClass() };

            using (MyObject obj = new MyObject(new MessageService()))
            {
                foreach (IEvent myEvent in eventList)
                {
                    myEvent.Execute();
                }
            }
        }
    }

    public interface IEvent
    {
        void Execute();
    }

    public class SomeClass : IEvent
    {
        public void Execute()
        {
            var stackTrace = new StackTrace();
            var stackFrames = stackTrace.GetFrames();
            var callingMethod = stackFrames[1].GetMethod();
            var callingType = callingMethod.DeclaringType;
        }
    }

    public class MyObject : IDisposable
    {
        public MessageService Service { get; }

        public MyObject(MessageService service)
        {
            Service = service;
        }

        public void Dispose()
        {
            Service.Stop();
        }
    }

    public class MessageService
    {
        public void Start() { }
        public void Stop() { } 
    }
}

I like your question, because it presents an interesting and an unusual situation, but I'm afraid that you won't be able to accomplish your task without going outside of conventional routines that C# has in its arsenal. You may be able to pull something off with unmanaged code, but that's a different topic altogether.

However, aside from it being an interesting question... look at what you're trying to do. You have MyObject, which obviously implements IDisposable and will call Dispose() at the end of that using statement, and you're trying to grab its reference from a different assembly. I don't think this is a good idea.

I suggest revisiting your design and make use of things such as an optional parameter. May not be the "perfect" solution for your situation, as you'll pass it to every Execute in that foreach loop, but it's better than jumping through a thousand fiery hoops of debug API's.

Community
  • 1
  • 1
B.K.
  • 9,982
  • 10
  • 73
  • 105
0

"If a class that implements IEvent does not/can not implement all the methods specified by IEvent the same way as they are declared in IEvent, that class should not implement IEvent in the first place." - Sweeper

So there's probably something wrong with your design of the whole program. I think you better revise your design a little bit and change some relationships between the classes and interfaces.

If you don't want to do that, there is another (not recommended) way to solve this problem.

Suppose your method caller is of type MethodCaller. You just change the declaration of Execute in the interface to this:

void Execute(MethodCaller obj = null);

And all the classes that implement IEvent can ignore the parameter except the class you mentioned in your question.

Sweeper
  • 213,210
  • 22
  • 193
  • 313