忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

v4.0でv2.0のアセンブリを使うとFileLoadExceptionがスローされる問題 (Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.)

このブログでも何度か取り上げましたが、独立して記事にしておきます。
VS2010などで、ランタイムバージョンが2.0のアセンブリを参照して使うと、次のような例外が出ます。

FileLoadException.PNG


FileLoadExceptionです。
ようは、アセンブリのバージョンが違うのでだめですよと言っているのです。

この問題を解決するには、.configファイルを使います。
バージョン2も使いますよと教えてあげるのです。

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0"/>
  </startup>
</configuration>

このファイルを作って、プログファムを実行するとさっきのような例外はスローされなくなるはずです。



SlimDXでは

さて、私がこの問題に直面したのはSlimDXを使っている時でした。
SlimDXとはDirectXのC#用ラッパーライブラリーです。

このSlimDXのアセンブリを参照に追加しようとしている時です。
どうも一見SlimDXにはランタイムバージョン4.0のものが存在しないように見えたのです。
あたかも2.0のアセンブリだけであるかのように。

IsSlimDXOnlySupportV2.0.png

クリックして上の画像を拡大してみてください。
SlimDXにはランタイムバージョンが2.0しか無いように、一見見えます。
ですから私はそのままこのアセンブリをv4.0のプロジェクトに追加して、FileLoadExceptionに悩まされました。
冒頭のようにApp.configを追加しなければならなかったのです。

そんなわけで「なんでSlimDXはv4.0対応していないのだろう?不親切だなー。これさえなければいいライブラリなのに・・・」と思ったものです。
が、違うのです。
v4.0のものもちゃんと有ります。

思い出すだけで恥ずかしいのですが、これはRuntimeというところをクリックすると、v4.0のアセンブリが出てくるのです。
v4.0のものが先頭に出てきてからSlimDXで検索すると、きちんと出てきます。

SlimDXAlsoSupportsV4.0.png

ですから、実はSlimDXを使うときにApp.configファイルを作る必要など全くなかったのです。
うーん反省です。












拍手[2回]

PR

C#でRubyっぽいループを書く

(この記事を書き終わってもしやと思いググッてみると
どうやら同じことを考えている人がすでに居たようです。残念)


4,5年前Rubyを使ったことがあったのですが、
ループがけっこう簡単です。
5.times { |i|
	puts i
}
Rubyを使ったことのない方のために言うと、
上の実行結果は
0
1
2
3
4

です。

これはたぶんC#よりも初学者にとって分かりやすいと思います。
なにせ「5回繰り返せ!」というのが
5.timesからぐっと伝わってくるからです。
(そう思いませんか!?)

私が初めて覚えたプログラミング言語はCでしたが、
当時中学生の身としてforはいささか複雑すぎました。
結果forに引っかかったことを覚えています。

C#でクロージャを使ってループ

しかしここで言いたいのはC#はダメだということではありません!
C#でもRubyのような書き方ができるからです。

まずC#で普通にループを書こうと思えば
以下のようにforを使います。
class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            System.Console.WriteLine(i);
        }
    }
}


結果は、上に書いたrubyのコードと同じです。
数字を0から4まで出力します。

C#でRubyのように書くにはどうすればいいでしょう?
ここで拡張メソッドの登場です。

static class Int32Extension
{
    public static void Times(this int loopCount, System.Action<int> loop)
    {
        for (int i = 0; i < loopCount; i++)
        {
            loop(i);
        }
    }
}


皆さんご存知のとおり上のように書くと、
System.Int32構造体に拡張メソッドを追加できます。
つまり以下のようにかけるのです。
class Program
{
    static void Main(string[] args)
    {
        5.Times(i => System.Console.WriteLine(i));
    }
}

いかがでしょうか。
forを使うよりシンプルになっているのは確かです。

ただ・・・これは良いものなのでしょうか悪いものなのでしょうか?
なるほどシンプルで書きやすいというのはそのとおりでしょうが、
forより明らかにパフォーマンスが落ちそうです。
もっともパフォーマンスはネックになるところ以外では気にするべきではありません。

では読みやすさはどうでしょうか?
個人的にこれを使ってみて、
「書きやすいけれどももしかするとほんの少し読みにくいかもしれない」
といった感想です。
読みにくいかもしれないというのはデリゲートに{}を使ったとき);と重なって
ちょっと汚く見えるかもしれないということです。

ただ、Timesの他にもUpToメソッドなどを書いて使ってみたのですが
そちらは});がごちゃごちゃしているのを差し引いても
かなり読みやすくなったと思います。

個人的見解としては、今後もループをたくさん使わなければいけないときにはこれを使うと思います、
といったところでしょうか。

拍手[0回]



Visual C# 2008 Express EditionでWPFアプリケーションを作っているときツールバーからアイテムをドラッグ&ドロップできない

先月は引越しやら何やらでゴタゴタまみれですっかりブログのサボり癖がついてしまいました。
リハビリとしてまずはちょっとしたことを書いておきます。

WPFでアイテムをツールバーからドラッグ&ドロップできない

ちょっと前から気になっていたことがありました。
VC#2008でWPFを作ってるときに、アイテムをツールバーからドラッグ&ドロップできないという問題です。

WPF、つまりWindows Presentation Foundationは、その前世代(?)のWindowsFormsと同じように、ユーザーインターフェースをドラッグ&ドロップでかんたんに作ることが出来ます(少なくとも、そういうことになっています)。
creatingWPFApplication.jpg

左側にToolboxというのがあってその中にButtonとかCheckBoxとか色んなアイテムがあり、そのアイテムをマウスでドラッグ&ドロップすることで右側のウィンドウに貼り付けることが出来るわけですね。これはWindows Formsと同じです。

ところが、昨年からそうだったのですが、それをやろうとしても×印が出てドロップできませんでした(仕方が無くそのときはXAMLを手打ちしていました。ひどすぎる)。

解決方法

で、ちょっと調べてみたら出てきました。

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2431637&SiteID=1
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2616165&SiteID=1

次のような操作をXP Professionalでしたらドラッグ&ドロップが出来るようになったそうです(確かめました。僕もVistaで同じ操作をしたら確かにドラッグ&ドロップできるようになりました。でもまあ念のためやるのならバックアップをお願いします):

1.%userprofile%の中の以下のフォルダを削除する
    Local Settings\Application Data\Microsoft\VSCommon
    Local Settings\Application Data\Microsoft\VCSExpress
    Application Data\Microsoft\VCSExpress

(最後のApplication Data\Microsoft\VCSExpressは僕は消し忘れていたのですが、それでも上手くいきました)

2."My Document"で次のフォルダを削除
    Visual Studio 2008\Settings
    Visual Studio 2005\Settings

(Visual Studio 2005のほうは削除しなくてもいい気がしますが、ついついこっちも削除してしまい、検証は出来ませんでした)

3.regedit.exeでレジストリキー、HKEY_CURRENT_USER\Software\Microsoft\VCSExpressを削除する。

(これは必須です。これを削除しなければ相変わらずドラッグ&ドロップは出来ませんでした。これを削除して初めて、再びドラッグ&ドロップできるようになりました。)

3を終えてからVisual C# 2008 Express Editionを起動すると、初回起動のときの例の「数分かかることがあります」が表示され、
fewseconds.jpg(←例のダイアログ)
再びWPFで上手くアイテムをドラッグ&ドロップできるようになります。

検証していないのでなんともいえませんが、おそらくこの手順には無駄がかなりあります。本当に必要なのは、3とわずかなフォルダの削除だけだと思います。もしかしたら3だけでも上手くいくかもしれません


追記

メインコンピュータでもういっかい試したところ(上で試したのはセカンドのラップトップパソコン)、どうやら削除するフォルダは「1.」の"Local Settings\Application Data\Microsoft\VCSExpress"だけでいいっぽいですね。そのあと「3.」のレジストリキーの削除でうまくいきました。(さいしょは「3.」だけを行ったのですがムリでした。~~VCSExpressを削除して、また「3.」をやって、はじめてうまくいきました)

拍手[0回]


"Orcas"とC# 3.0 その2 拡張メソッド

C# 3.0 には拡張メソッドなるものがあるそうで、
あるクラスのメソッドを後から増やせるそうです。
using System;

class Person { public int Age { get; set; } }

static class PersonExtension
{
    public static bool IsAdult(this Person person) {
        return person.Age >= 20;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person child = new Person { Age = 10 };
        Person father = new Person { Age = 35 };
        Console.WriteLine(child.IsAdult()); //False
        Console.WriteLine(father.IsAdult()); //True 
    }
}

一見したところこれはクラスの凝集性を破壊するだけ
のように思えます。
別々に書く意味は全くありません。
PersonクラスにIsAdultメソッドも書くべきです。
実際、公式でもあまり軽々しく使うなと言われているようです。

ではなぜ拡張メソッドが存在するかと言うと、
1つにはインターフェースが実装しやすくなる
と言うのがあるようです。

using System;
using System.IO;

interface IPerson { int Age { get; set; } }
class Person : IPerson { public int Age { get; set; } }
class PersonFile : IPerson
{
    public int Age
    {
        get { return Int32.Parse(File.ReadAllText("person.txt")); }
        set { File.WriteAllText("person.txt", value.ToString()); }
    }
}

static class PersonExtension
{
    public static bool IsAdult(this IPerson person)
    {
        return person.Age >= 20;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person child = new Person { Age = 10 };
        PersonFile father = new PersonFile { Age = 35 };
        Console.WriteLine(child.IsAdult());
        Console.WriteLine(father.IsAdult());
    }
}
とりあえずPersonクラスに似た、PersonFileクラスを作りました。
メモリではなくファイルに年齢を格納します。
(ここは本当はファイルではなくネットワークに接続する
Proxyパターンにするべきなのでしょうけど長くなると困りますからね。)

もしPersonのインターフェースを作った場合、
それを使ってIsAdult()を呼べるべきです。

しかしそうすると、もし拡張メソッドを使わなかったら、
全ての実装したクラスでも
Age >= 20;
とやらなければなりません。

新たにクラスを1つ作るたびに重複が多くなってしまいます。

拡張メソッドを使えばその問題が解決されます。
Age >= 20;
は一ヶ所、拡張メソッドの中に集中します。
すばらしい。

実際の.netライブラリではオブジェクトのコレクション(?)
を表すIEnumerableインターフェースが拡張されています。

IEnumerableはオブジェクトを列挙する能力しかありませんが、
System.Linq.Enumerableによってさまざまな便利な能力を
付与されています。

拍手[1回]


        
  • 1
  • 2
  • 3