1

I have two projects: Engine, Client. During the execution of the Engine1.cs class I would like to open a windows form from the ClientAction class by passing it objects from the Engine.cs. Client references the Engine project.

namespace Engine {

   public Class Engine1 {
     public Engine1() {
      }

//what I would do if I could reference the Client project
     ClientAction.OpenForm(obj1, obj2);
   }
}

using Engine;

namespace Client { 

   public Class ClientAction {

     public ClientAction() { }

      public OpenForm(object obj1, object obj2) {

         Form1.Open(obj1, obj2){
            ...
         }
      }
   }
}
undertree
  • 79
  • 1
  • 1
  • 10
  • 1
    Make a commons project containing the type. – LuckyLikey Jun 13 '19 at 09:49
  • Possible duplicate of [How to solve circular reference?](https://stackoverflow.com/questions/6928387/how-to-solve-circular-reference) – Renat Jun 13 '19 at 09:51
  • 1
    Can you not fire an event from `Engine` class for which a class in `Client` project subscribes and performs the required action? – Suresh Jun 13 '19 at 10:19

4 Answers4

1

You can do this using reflection and class System.Activator ("mscorlib.dll" assembly). Define your classes as below:

In project Engine

using System;
using System.Reflection;

namespace Engine
{
    public class Engine1
    {
        public Engine1()
        {
            var clientAction = Activator.CreateInstance(
                Type.GetType("Client.ClientAction, Client"), new object[] { });
            MethodInfo methodInfo = clientAction.GetType().GetMethod("OpenForm");
            var arg1 = new object();
            var arg2 = new object();
            methodInfo.Invoke(clientAction, new object[] { arg1, arg2 });
        }
    }
}

In project Client, class ClientAction

namespace Client
{
    public class ClientAction
    {
        public ClientAction() { }

        public void OpenForm(object obj1, object obj2)
        {
            new Form1()
            {
                Text = "OpenForm(object obj1, object obj2)"
            }.Show();
        }
    }
}

Now in project Client, you can test it in class Program like this:

using System;
using System.Windows.Forms;

namespace Client
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var engine1 = new Engine.Engine1();
            Application.Run(new Form1());
        }
    }
}

Another option is using delegates. You must create a static event in project Engine and in project Client subscribe a handler to this event which will invoke required method.

In project Engine

the delegate:

namespace Engine
{
    public class OpenFormEventArgs
    {
        public object Obj1 { get; set; }
        public object Obj2 { get; set; }
    }

    public delegate void OpenFormEventHandler(object sender, OpenFormEventArgs e);
}

class Engine1

namespace Engine
{
    public class Engine1
    {
        public static event OpenFormEventHandler OpenForm;

        public Engine1()
        {
            var obj1 = new object();
            var obj2 = new object();
            OpenFormEventArgs e = new OpenFormEventArgs() { Obj1 = obj1, Obj2 = obj2 };
            OpenForm?.Invoke(this, e);
        }
    }
}

In project Client, class ClientAction

namespace Client
{
    public class ClientAction
    {
        public ClientAction()
        {
            Engine.Engine1.OpenForm += Engine1_OpenForm;
        }

        private void Engine1_OpenForm(object sender, Engine.OpenFormEventArgs e)
        {
            OpenForm(e.Obj1, e.Obj2);
        }

        public void OpenForm(object obj1, object obj2)
        {
            new Form1()
            {
                Text = "OpenForm(object obj1, object obj2)"
            }.Show();
        }
    }
}

Now in project Client, you can test it in class Program like this:

using System;
using System.Windows.Forms;

namespace Client
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            var clientAction = new ClientAction();
            var engine1 = new Engine.Engine1();
            Application.Run(new Form1());
        }
    }
}
DotNet Developer
  • 2,973
  • 1
  • 15
  • 24
0

You can define an interface in the Engine project, with methods to call various actions. When client creates the engine, make the engine require an instance of that interface. The client project implementing the interface will then handle the events i.e. create forms.

Another approach is expose C# events from your Engine1 class. Subscribe to these events in Client, create your forms.

In both approaches, events raised by Engine class will be handled in the referencing Client project.

Interfaces are better when the projects are tightly coupled, or when you want to return values to the engine (C# events support multiple subscribers, and subscribing is optional).

Soonts
  • 20,079
  • 9
  • 57
  • 130
0

Create interfaces for the types you want to pass to the client. Locate these interfaces in the client project.

This way, you only need one reference from Engine to Client. The implementations of the interfaces are linving in the Engine project.

You can call the client from Engine and pass the objects needed by the client.

This result in multiple improvements. You can implement different engines. The client doesn't need to know, how the engine works. And it's generally a cleaner and more flexible design.

Note: The classes and interfaces should all be in there own file.


Engine

using Client;

namespace Engine
{
    public class Engine1
    {
        public Engine1()
        {
            var myObject = new MyObject
            {
                SomeProperty = null
            };

            ClientAction.OpenForm(myObject);
        }
    }

    public class MyObject : IMyObject
    {
        public object SomeProperty { get; set; }

        public void DoSomething()
        {
            // do something
        }
    }
}

Client

namespace Client
{
    public class ClientAction
    {
        public ClientAction() { }

        public OpenForm(IMyObject myObject)
        {
            myObject.DoSomething();
            Form1.Open(myObject.SomeProperty);
        }
    }

    public interface IMyObject
    {
        object SomeProperty { get; set; }
        void DoSomething();
    }
}
Emaro
  • 1,397
  • 1
  • 12
  • 21
-1

No, this is not possible and it suggests poor design choices on the developer's part.

Code that is shared between two projects/applications or even libraries should be in their own, separate project. This is usually called Common.

In essence you'd have the following structure:

 ...\MySolution\
     Project1\
          ...
     Project2\
          ...
     Common\
          CommonClass.cs

Within this structure, Project1 & Project2 can reference Common and use the same code base.

Another thing you should look out for, are potential circular dependencies, which can occur if (for example) a class in Project1 relies on a class in Common, and a class in Common relies on a class in Project1.

SimonC
  • 1,547
  • 1
  • 19
  • 43