質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.03%

.NET Standard クラスライブラリを参照すると出力フォルダに.dllが大量に出力される理由

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 3,402

g_uo

score 208

 質問

.NET Standard クラスライブラリを .NET Framework のプロジェクトで参照したとき、.NET Framework の出力先フォルダにDLLが大量に出力されます。
このようにたくさんのDLLが出力される理由をご教示いただきたいです。
また、もしこのようなDLL出力をやめる方法があれば合わせてご教示いただければ幸いでございます。

出力フォルダ

 環境情報

OS: Windows 10 Pro 64bit (1803)
IDE: Visual Studio Community 2017 (v15.8.1)
.NET Framework: 4.6.1
.NET Standard: 2.0
言語: C#

[サンプルプロジェクト情報]
HelloStandard : クラスライブラリ (.NET Standard)
HogeConsole: コンソールアプリ (.NET Framework)

 私見

認識の間違いは是非ご指摘頂戴したいのですが、
私の考えとしては、netstandard.dllのもつ依存関係が原因かな、と思っております。
ただ、.NET Standard 2.0 は .NET Framework 4.6.1 をサポートしており、Frameworkだけがもつ部分は別として、Standard と Framework で異なる依存関係をもつということがしっくりきません。
(しかしながら、そもそも理解不足もあると思います)

 質問の背景

実行ファイルを配布する際、何らかの不手際でDLLのコピー漏れが発生するリスクが高く、依存関係のあるファイルは少なくしたいという思いがあり、質問いたしました。
自動化すれば問題ないか...とも思いましたが、その自動化スクリプトのチェック自体が煩雑になることと、今後リリースの度、たくさんのDLL(もちろん改造に伴って追加されるDLLも)に漏れがないかどうか確認するのは骨が折れると思っています。

.NET Standardのクラスライブラリを作成しようと思った理由としては、時代の流れというのが一番の理由ですが、既存資産を.NET Standardクラスライブラリに移行し、Windows以外のプラットフォームにも展開可能な状態にしておきたいという判断からです。

 サンプルプロジェクトのソースコード

プロジェクト: HelloStandard

HelloStandard.cs

using System;

namespace Standard
{
    public class HelloStandard
    {
        public HelloStandard(string message)
        {
            Message = message;
        }

        public string Message { get; }

        public void PrintOut()
        {
            Console.WriteLine(Message);
        }
    }
}

HelloStandard.csproj

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

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <RootNamespace>Standard</RootNamespace>
  </PropertyGroup>

</Project>

プロジェクト: HogeConsole

Program.cs

using Standard;

namespace HogeConsole
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var helloStandard = new HelloStandard("Hello .NET Standard!!");
            helloStandard.PrintOut();
        }
    }
}
Hello .NET Standard!!

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
</configuration>

HogeConsole.csproj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid></ProjectGuid>
    <OutputType>Exe</OutputType>
    <RootNamespace>HogeConsole</RootNamespace>
    <AssemblyName>HogeConsole</AssemblyName>
    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HelloStandard\HelloStandard.csproj">
      <Project></Project>
      <Name>HelloStandard</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

以上です。どうぞよろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+3

こんにちは。

ご存じの通り、NETStandardには「実装がありません」。あるのは「大量のクラスとメソッドシグネチャによるAPI定義」だけです。具体的には、「中身(実装)が空っぽのクラス定義だけを詰めたアセンブリ」の集まりで構成されています。
どうしてそんなことになっているかというと、NETStandardは最初からクロスプラットフォームライブラリを作成するために設計されたもので、「各プラットフォームのAPIへの直接アクセス」ではなく、「薄いアセンブリを介した呼び出し」とすることで実装の差し替えを可能としているのです。C#で言う、オブジェクトの直接使用を避けてインターフェースを一枚挟んでいるような状態をイメージすれば良いです。そして、そのインターフェース群こそが、画像にある大量のDLLファイルの正体です。

おそらくですが、それらのDLLのうち、netstandard.dllが追加したアセンブリをごっそり削除してもアプリは動くんじゃないかなーと思います。ランタイムが.NET Frameworkであるなら、参照アセンブリを外しても.NET Framework側の実装アセンブリを直接参照することになるはずです。(たぶん!)


と言っても、そんなこわいことできないので、ここは別の視点から、

「Visual Studioのpublish機能を使ってリリースを作成する」

というのをオススメしてみます。
publishは通常のビルドと違って、アプリに必要なアセンブリやデータ類を一カ所にまとめて出力するもので、これをそのままpackして配布や更新に利用するのが最も安全かつ確実な手段となります。
.NET Core SDKを利用していてdotnetコマンドが使用可能な状態であれば、dotnet publishコマンドで同様のことを行うこともできるので、スクリプト化も十分可能です。


もう一つの提案としては、

「NETStandardのライブラリをnet461とマルチターゲットにする」

という方法があります。
これは、ライブラリをNETStandardとの互換性を保ったまま、.NET Frameworkに対してはnet461のアセンブリが提供されるようにできるため、netのアプリに対してNETStandardのインターフェース参照がごっそり追加されるということ自体を回避できます。
こちらの方法を採用する場合、ライブラリの「開発環境」が.NET Frameworkに対応している必要があります。NETStandardシングルであればLinux上でも開発可能ですが、netとクロスにするとWindowsでしかビルドできなくなります。


どちらの方法が優れているということもないですが、個人的には、プロジェクトは変更せず、発行プロセスを工夫する方が、選べるならその方が懸念点が少なくて良いですね。
もし、マルチターゲットを選んだとしても、配布に向けたビルドプロセスを見直すことは考えてみても良いのではないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/03 11:09

    tamotoさん、未だ情報少ない中、貴重な回答ありがとうございます。
    まず、私のNET Standardに関する知識が不足しておりました。ご教示いただいた、インターフェース群という考え方でDLL大量出力の疑問も解決しましたし、そう考えれば自然なことと理解できました。

    また、ご提案いただいた2つ方法について当方の環境で実際試してみました。
    結果、マルチターゲットのほうで出力DLLをなくすことができましたが、プロジェクトファイルに記載するReferenceタグで混乱しそうなことと、自分以外が作ったライブラリの依存関係まで正確に把握しきれない可能性もあると思いました。なのでpublishを用いてpackするほうが、ファイル数は増えども結果的に安全な方法であるという考えに至りました。
    よって、今後publishを基本にして、安全かつ手数の少ない方法を模索してみます。

    また機会がございましたらご助力いただければ幸いでございます。

    余談ですが、tamotoさんがteratailの.NET Standardタグ初めての回答者さんです!
    ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる
  • トップ
  • C#に関する質問
  • .NET Standard クラスライブラリを参照すると出力フォルダに.dllが大量に出力される理由