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

时间:2014-12-12 23:54:29

标签: c# main

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

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

抽象示例:

使用Program.cs考虑项目1:

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类的Project 2:

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

要指出的事情:

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

目的:

项目2应该编译成一个可执行文件,运行Project 1的Main函数而不再编写任何代码(没有样板启动/设置代码)。然后可以有项目3,4,5 ......,它们都实现了他们的插件特定代码,导入项目1并作为独立实例运行。

这可能吗?或者我是否仍然需要在每个调用导入项目的启动代码的项目中创建一个main方法?非常感谢提前!

3 个答案:

答案 0 :(得分:1)

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

然后,您可以将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

请记住,这是一个非常基本的示例,并且应该有一个经过深思熟虑的界面,它实际上只包含基础知识,并且可能会给运行插件的程序提供一些反馈(尽管这不应该& #39;是一项要求)。还提供插件版本,也许是更新Url,如果您的插件可以由第三方提供商维护或实施,这样的事情可能很方便......

答案 1 :(得分:0)

我认为启动方法的要求是它的签名必须是public static void,并且它需要有一个string[]参数。它也可能需要被命名为#34; Main&#34;,但我对此表示怀疑。如果方法符合这些要求,则可以选择它作为项目属性中的启动方法。

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

很抱歉,如果这听起来有点模糊,而且是一个&#34;插件架构&#34;这不是一个微不足道的话题。

另一种方法是使用System.Diagnostics.Process.Start(string)方法启动您的&#34;插件&#34;作为一个独立的计划。

答案 2 :(得分:0)

我不确定你要求的是什么。每个C#项目都是.exe.dll.dll没有主要方法,但.exe需要一个。{1}}。 Here's the link describing what it should look like

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

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

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