【问题标题】:Auto-generate main method from referenced assembly从引用的程序集自动生成主要方法
【发布时间】:2019-12-26 00:15:38
【问题描述】:

我正在编辑我的问题,我认为这有点令人困惑,并且没有解释我的意图。

编辑:

我的目标是当我的HelloWorld 应用程序引用MyClassLibrary 时,我的代码不会编译,因此我确保在运行 main 方法之前初始化一些代码。 有点像类的构造函数。当我引用MyClassLibrary 时,我希望在运行HelloWorld 应用程序的main 方法之前在其中运行一些代码。 NUnit 具有类似的功能。当我的HelloWorld 应用程序引用 NUnit 时,我收到错误:Error CS0017 Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point. 正如@Alex 指出的那样,NUnit 创建的 Main 方法是自动生成的。我想用一些自定义代码自动生成一个主方法。我怎样才能从 MyClassLibrary 做到这一点,而不像 NUnit 那样在我的 HelloWorld 应用程序上做任何事情?


老问题:

我想执行NUnit 测试执行的相同行为,它阻止使用Main 方法。在这种情况下,我需要的错误是一件好事。让我解释一下我的意思。

  1. 我创建了一个面向 .net core 的 hello world 应用程序

项目文件:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

</Project>

代码文件:(默认hello world c#代码)

  1. 如果我随后运行该应用程序,它运行良好

  2. 添加对NUnit 的引用,我的项目文件现在包含。

.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="NUnit" Version="3.12.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
  </ItemGroup>

</Project>    
  1. 当我尝试编译项目时出现错误:

错误 CS0017 程序定义了多个入口点。使用 /main 编译以指定包含入口点的类型。

这意味着还有另一个Main 方法。该方法可能位于我引用的NUnit nuget 包中。这是我试图复制的错误!


现在这就是我尝试复制相同错误的方式:

  1. 我在我的 hello world 应用程序中删除了没有引用 NUnitNUnit nugget 包。

  2. 使用以下代码创建项目ClassLibrary1

.

public class MyLib
{
    static void Main()
    {
        Console.WriteLine("fooooo");
        // do something
    }
}
  1. 让我的 hello world 应用程序引用该项目:

即使有 2 个 Main 方法,我编译时也不会出错!

NUnit 如何防止使用Main 方法?如何复制相同的行为?我想创建一个程序集,当被引用时它会阻止执行Main 方法。

【问题讨论】:

  • 这对我来说并不奇怪。您无法使用单元测试框架测试 exe 文件 - 测试框架测试时的应用程序。
  • 我不需要测试。我需要防止我的应用程序有一个 Main 方法 @500-InternalServerError
  • 取决于项目类型,类库不会执行任何主要方法。但是像控制台项目这样的东西需要一个入口点

标签: c# .net-core compiler-errors compilation


【解决方案1】:

我认为您应该学习如何在同一个解决方案下制作多个项目。 所以helloworld是主要项目。 然后创建新的测试项目 helloworld.test 作为测试项目使用在那里添加对 NUnit 的引用。 现在一切正常,您可以将启动项目更改为 helloworld.test 并调试或从 Visual Studio 或命令行运行它。 无论如何,我从未在专业编码的主要项目中看到过测试项目。可能是为了测试我们注释主要方法并运行测试用例。 测试项目也是可执行的。

【讨论】:

    【解决方案2】:
    • 只是 Microsoft.NET.Test.Sdk 导致构建失败。
    • &lt;GenerateProgramFile&gt;false&lt;/GenerateProgramFile&gt; 添加到&lt;PropertyGroup&gt; 中可以使其编译并正常工作。
    • 但是向应用程序添加另一个带有static void Main 的类会使构建再次失败,无论&lt;GenerateProgramFile&gt;
    • 在您的示例中构建失败,因为Microsoft.NET.Test.Sdk 在编译之前向您的应用程序添加了一些自动生成的代码。该代码位于...\.nuget\packages\microsoft.net.test.sdk\16.2.0\build\netcoreapp1.0\Microsoft.NET.Test.Sdk.Program.cs。这是一个与另一个Main 的课程:

    // <auto-generated> This file has been auto generated. </auto-generated>
    using System;
    [Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode]
    class AutoGeneratedProgram {static void Main(string[] args){}}
    

    顺便说一句:在另一个程序集中拥有Main 方法是绝对合法。您不能在一个 exe 中包含 2 个Mains。但是您可以像这样在 dll 中拥有 任意 个它们:

    public class Class1
    {
        public static void Main() { }
    
        public static void Main(string[] args) { }
    }
    
    public class Class2
    {
        public static void Main() { }
    
        public static void Main(string[] args) { }
    }
    

    它编译。


    更新:

    我找到了解决方案。 安装nuget,而不仅仅是添加引用。

    1. 创建一个.NET Core Class Library 并将其命名为MyCoreLib
    2. 添加MyCoreClass

    namespace MyCoreLib
    {
        public static class MyCoreClass
        {
            public static void Initialize()
            {
                System.Console.WriteLine("Initialized from 'MyCoreLib'");
            }
        }
    }
    

    1. 构建库。
    2. 创建以下文件结构:

    ├───nuget
    └───src
        │   MyCoreLib.nuspec
        │
        ├───build
        │   └───netcoreapp2.1
        │           ForcedEntryPoint.cs
        │           MyCoreLib.targets
        │
        └───lib
            └───netcoreapp2.1
                    MyCoreLib.dll
    

    MyCoreLib.nuspec

    <?xml version="1.0" encoding="utf-8"?>
    <package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
      <metadata>
        <id>MyCoreLib</id>
        <version>1.0.0</version>
        <authors>MyCoreLib</authors>
        <owners>MyCoreLib</owners>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>Some description here</description>
        <dependencies>
          <group targetFramework=".NETCoreApp2.1" />
        </dependencies>
      </metadata>
    </package>
    

    ForcedEntryPoint.cs

    //╔════════════════════════════════════╗
    //║ This code was added automatically. ║
    //║    Do not change or remove it.     ║
    //╚════════════════════════════════════╝
    public static class ForcedEntryPoint
    {
        public static void Main(string[] args)
        {
            MyCoreLib.MyCoreClass.Initialize();
        }
    }
    

    MyCoreLib.targets

    <Project InitialTargets="ForceEntryPoint" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup>
        <OutputType>Exe</OutputType>
      </PropertyGroup>
      <PropertyGroup>
        <ForcedEntryPoint Condition="'$(ForcedEntryPoint)' == ''">$(MSBuildThisFileDirectory)ForcedEntryPoint$(DefaultLanguageSourceExtension)</ForcedEntryPoint>
        <ForceEntryPoint Condition="'$(ForceEntryPoint)' == ''">true</ForceEntryPoint>
      </PropertyGroup>
      <Target Name="ForceEntryPoint" Condition="'$(ForceEntryPoint)' == 'true'">
        <ItemGroup>
          <Compile Include="$(ForcedEntryPoint)"/>
        </ItemGroup>
      </Target>
    </Project>
    

    1. 使用NuGet Commandline 构建这样的包:

    D:\nugetwalkthrough\nuget>D:\nugetwalkthrough\nuget.exe pack D:\nugetwalkthrough\src\MyCoreLib.nuspec
    

    1. 创建一个.NET Core Console App 并确保它有效。
    2. 安装创建的包。
    3. 尝试运行应用程序并得到错误:

      CS0017 程序定义了多个入口点。使用 /main 编译以指定包含入口点的类型。

    4. 从应用程序中删除Main 方法,运行它并看到它打印Initialized from 'MyCoreLib'
    5. Main方法放回应用程序并更改项目文件,使&lt;PropertyGroup&gt;包含&lt;ForceEntryPoint&gt;false&lt;/ForceEntryPoint&gt;
    6. 现在它从自己的 Main 方法编译和打印 Hello World!
    7. &lt;ForceEntryPoint&gt; 更改为true 使其再次使用另一个入口点(不是应用程序)。

    【讨论】:

    • 是的,Alex 我想在我的 Main 方法运行之前添加一些初始化代码。非常感谢您的帮助。
    • 我在引用的程序集上添加了一个 T4 模板,该代码在引用的程序集上创建了一个 Main 方法。 我需要该 Main 方法位于我的主要可执行文件上。我怎样才能像 NUnit 一样做到这一点? 创建 T4 模板时,我的代码仍然可以编译。我想确保它不会编译,以便我记得我必须运行一些初始化代码
    • @TonoNam,太棒了!带有另一个 Main 的自动生成的 cals 添加到您的控制台应用程序中,而不是引用的程序集中。这就是应用程序无法编译的原因。这是一种包装技巧。我会尝试检查它是如何工作的。
    • Alex 我得等一天才能给你赏金。一旦堆栈溢出允许我,我将在星期一接受赏金。非常感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2013-05-17
    • 1970-01-01
    • 1970-01-01
    • 2010-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多