読者です 読者をやめる 読者になる 読者になる

nuits.jp blog

C#, Xamarin, WPFを中心に書いています。Microsoft MVP for Visual Studio and Development Technologies。なお掲載内容は個人の見解であり、所属する企業を代表するものではありません。

Xamarin.FormsでReleaseビルドしたら「Could not load file or assembly~」で実行時例外がでる場合の話

昨晩、@omanukeさんがこんな記事をエントリーされました。

d.hatena.ne.jp

で、これが発端でちょっと話が盛り上がったので、その結果分かった事をちょっとまとめたいと思います。

さて、以下のXAMLを見てみてください。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:system="clr-namespace:System;assembly=System.Runtime.Extensions"
             x:Class="XFApp2.MainPage">
  <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center">
    <Label.FormattedText>
      <FormattedString>
        <Span Text="Before" ForegroundColor="Blue"/>
        <Span Text="{x:Static system:Environment.NewLine}" />
        <Span Text="After" ForegroundColor="Red"/>
      </FormattedString>
    </Label.FormattedText>
  </Label>
</ContentPage>

これをDebugビルドするとこうなります。

f:id:nuitsjp:20160730205222p:plain

まぁ想定通りですね。
上のようなコードを書くことで、一つのラベルの中で複数の表現(色とか諸々)や改行を含んだ文字列を表示することができます。

ところが、上のコードをリリースビルドすると以下の例外が発生します。

Caused by: android.runtime.JavaProxyThrowable: System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime.Extensions' or one of its dependencies
File name: 'System.Runtime.Extensions'

結論から言ってしまうと、つまりこういうことだそうです。

あ~んど

先ほどのXAMLのうち、問題の箇所を見てみましょう。

~略
xmlns:system="clr-namespace:System;assembly=System.Runtime.Extensions"
~略~
<Span Text="{x:Static system:Environment.NewLine}" />

改行を出力するために、System.EnvironmentクラスのNewLineプロパティを利用しています。
この時に、「system:」のネームスペースをたどると、System.Runtime.Extensionsを見ています。 しかし、System.Runtime.ExtensionsはPCLのFaçadeとやらで(良く分かってないので、あとでちゃんと勉強しなきゃ)、実体はmscorelib側に存在します。 この為、Releaseビルド時にLinkerがSystem.Environmentクラスを不要としてリンク対象から除外してしまうため実行時にエラーが出てしまう。
逆にDebugビルドだとコンパイル時にそこまで最適化しない為動作する。。。という事のようです。

動作させるためには対処方法は二つあって

  • xmlns:system="clr-namespace:System;assembly=mscorlib"
    で宣言する
  • {x:Static system:Environment.NewLine}

    {x:Static x:Environment.NewLine}

で宣言すれば動作します。
どっちも@ticktackmobileさんの受け売りですがw

ちなみに何でこんなことが起こったかというと

  1. x:Environment.NewLineとうつと、ReSharper先生がちゃんとネームスペース宣言せいや!と怒る
  2. ReSharper先生にお任せで修正すると、System.Runtime.Extensionsで宣言してくれちゃう

というのが発端でした。。。
ReSharper先生からするとPCLプロジェクト上からは、System.Runtime.Extensionsのクラスとして見えるので仕方ないでしょうけど、罠ですよね。。。

ということで、Debugビルドでは動いていたのに、Releaseビルドしたら「Could not load file or assembly~」が出て落ちる場合は、これを疑ってみてはいかがでしょうか?

と言う分けで、今日はここまで。
それではまた!