是否可以使用 main 方法导入另一个 C# 项目并将其设置为入口点

本文关键字:设置 入口 项目 可以使 main 方法 另一个 导入 是否 | 更新日期: 2023-09-27 18:34:14

是否可以在没有main方法的情况下在C#中创建项目,并导入另一个具有main方法的项目,并将入口点设置为导入项目的main 方法?

这样做的目的是提供一个包含其主方法和所有启动代码的库,只需要几个"插件"方法。这将最大限度地减少样板(特别是启动(代码。

抽象示例:

考虑具有计划的项目 1.cs:

namespace Project1 {
  public class Program {
    public static void Main() {
      Console.WriteLine("All your Main are belong to us");
      Plugin pluginClass = MagicallyGetInstanceOfPluginClassProbablyThroughInjection();
      pluginClass.DoSomethingSpecificDependingOnPluginClassDefinition();
    }
    private Plugin MagicallyGetInstanceOfPluginClassProbablyThroughInjection(){
      /*...*/
    }
  }
  public interface Plugin {
    void DoSomethingSpecificDependingOnPluginClassDefinition();
  }
}

现在考虑只有类 MyPlugin.cs 的项目 2:

namespace Project2 {
  using Project1;
  public class MyPlugin: Plugin {
    public void DoSomethingSpecificDependingOnPluginClassDefinition() {
      Console.WriteLine("I'm doing something specific!");
    }
  }
}

需要指出的事项:

  • 项目 1 只是一个库,可能是 nuget's
  • 导入项目 1 的是项目 2,而不是相反
  • 上面的 MyPlugin.cs 类是项目中唯一的类/文件(不包括清单、应用程序配置等(

目的:

项目

2 应该编译成可执行文件,运行项目 1 的 Main 函数,而无需编写更多代码(没有样板启动/设置代码(。然后可以有项目 3、4、5、...它们都实现了特定于插件的代码,导入项目 1 并作为独立实例运行。

这可能做到吗?还是我仍然必须在每个项目中创建一个 main 方法来调用导入项目的启动代码?提前非常感谢!

是否可以使用 main 方法导入另一个 C# 项目并将其设置为入口点

您可以创建一个插件容器,用于扫描目录中的程序集并尝试加载它们。为此,您需要一个共享接口(您的程序和插件已知的接口。

然后,您可以将 DLL 添加到定义的插件目录中,或者您可以在主运行项目中引用项目。

接口的示例可以是:

public interface IStandAlone
{
    void Run();
}

和 1 或简单的实现可能是

public class Program1 : IStandAlone
{
    public void Run()
    {
        Console.WriteLine("Program1");
    }
}
public class Program2 : IStandAlone
{
    public void Run()
    {
        Console.WriteLine("Program 2");
    }
}

然后,您需要从当前程序集加载可能的程序集(如此示例中所做的那样(,或者通过扫描目录中可能具有你的类型的 dll 来加载可能的程序集。

扫描当前程序集以查找确定类型的任何实现的示例:

public class PluginContainer<T>
{
    Type targetType = typeof(T);
    public virtual IList<Type> GetMatchingTypes()
    {
        Assembly[] currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();
        IList<Type> items = new List<Type>();
        if (currentAssemblies == null || currentAssemblies.Length == 0)
        {
            Console.WriteLine("No assemblies found!");
            return items;
        }
        foreach (Assembly ass in currentAssemblies)
        {
            try
            {
                var types = ass.GetTypes();
                foreach (var t in types)
                {
                    if (t.IsInterface)
                    {
                        continue;
                    }
                    if (!(targetType.IsAssignableFrom(t)))
                    {
                        continue;
                    }
                    items.Add(t);
                }
            }
            catch (ReflectionTypeLoadException rtle)
            {
                /* In case the loading failed, scan the types that it was able to load */
                Console.WriteLine(rtle.Message);
                if (rtle.Types != null)
                {
                    foreach (var t in rtle.Types)
                    {
                        if (t.IsInterface)
                        {
                            continue;
                        }
                        if (!(targetType.IsAssignableFrom(t)))
                        {
                            continue;
                        }
                        items.Add(t);
                    }
                }
            }
            catch (Exception ex)
            {
                /* General exception */
                Console.WriteLine(ex.Message);
            }
        }
        return items;
    }
    public IList<T> GetPlugins()
    {
        IList<Type> matchingTypes = GetMatchingTypes();
        IList<T> items = new List<T>();
        if (matchingTypes == null || matchingTypes.Count == 0) 
        {
            Console.WriteLine("No matching types of {0} found", typeof(T).FullName);
            return null;
        }
        foreach (Type type in matchingTypes)
        {
            try
            {
                T nObj = (T)Activator.CreateInstance(type);
                items.Add(nObj);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error occured trying to run {0}'r'n{1}", type.FullName, ex.Message);
            }
        }
        return items;
    }
}

然后可以在 main 方法中使用它来扫描任何可用的插件,并执行它们:

static void Main(string[] args)
{
    PluginContainer<IStandAlone> container = new PluginContainer<IStandAlone>();
    var plugins = container.GetPlugins();
    foreach (var plugin in plugins)
    {
        plugin.Run();
    }
    Console.ReadLine();
}

最终作为输出给出:

Program1
Program 2

请记住,这是一个非常基本的例子,应该有一个经过深思熟虑的界面,它实际上只包含基础知识,并且可能会给运行插件的程序一些反馈(尽管这不应该是一个必需的(。还提供插件版本,也许是更新 URL,如果您的插件可以由第三方提供商维护或实现,这些东西可能会很方便......

我相信启动方法的要求是它的签名需要public static void并且需要具有单个string[]参数。它可能还需要命名为"主要",但我对此表示怀疑。如果方法符合这些要求,则应可在项目属性中选择该方法作为启动方法。

但是,启动方法是用于在启动独立可执行程序时运行该程序的方法。我相信您正在寻找的更多的是插件架构。您可以创建一个属性,并使用该属性标记入口点方法。然后,在服务中,您需要反映正在加载的插件程序集中的类,并找到标有自定义属性的方法,并调用相应的方法。

抱歉,如果这听起来有点模糊,但"插件架构"并不是一个微不足道的话题。

另一种方法是使用 System.Diagnostics.Process.Start(string) 方法将您的"插件"作为独立程序启动。

我不太确定你要什么。每个 C# 项目要么是.exe,要么是.dll.dll没有主方法,但.exe需要一个。这是描述它应该是什么样子的链接。

如果您有许多非常相似的应用程序,那么您可以在.dll项目中移动所有常见内容,并在所有应用程序中引用它。然后,您可以从每个.exe调用方法。在每个.exe中,您仍然会有一个Main()方法,但它只包含一行调用公共实现。

或者你可以做一些类似插件架构的事情,你有一个.exe,所有其他应用程序都是.dll项目,这些项目由.exe根据需要加载和执行。

六个一个,六个另一个,最后都是一样的。