2014年6月13日金曜日

NET Framework のバージョンを表すシンボル定義

色々探したが、よい物が見つからなかった。
なので、作ってみた。

<None Include="App.config" />
  </ItemGroup>
  <Import Project="$(SolutionDir)NetFrameworkVersion.targets" />
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
  <PreBuildEvent>

これで、以下のシンボルが定義される。

  • NET_20
  • NET_30
  • NET_35
  • NET_40
  • NET_45
  • NET_451
  • NET_20_OR_GREATER
  • NET_30_OR_GREATER
  • NET_35_OR_GREATER
  • NET_40_OR_GREATER
  • NET_45_OR_GREATER


このファイルを、ソリューションフォルダに置くのを忘れずに。
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    
    <!-- NET Frameworkのバージョンに関する定数を定義する。 -->
    <!-- NET_20, NET_30, NET_35, NET_40, NET_45, NET_451, -->
    <!-- NET_20_OR_GREATER, NET_30_OR_GREATER, NET_35_OR_GREATER, NET_40_OR_GREATER, NET_45_OR_GREATER -->

    <!-- メジャーバージョン、マイナーバージョン、ビルド番号の結果を入れる -->
    <VerMajor>0</VerMajor>
    <VerMinor>0</VerMinor>
    <VerBuild>0</VerBuild>

    <!-- Pos1:最初のピリオドの位置 / Pos2:次のピリオドの位置 / Pos1n,Pos2nは、それぞれのPosに+1している -->
    <Pos1>-1</Pos1>
    <Pos1n>-1</Pos1n>
    <Pos2>-1</Pos2>
    <Pos2n>-1</Pos2n>

    <!-- v4.5.1とかv2.0とか入っているので、まずは'v'を除去 -->
    <Tmp>$(TargetFrameworkVersion.Replace('v', ''))</Tmp>

    <!-- ピリオドがなければPos1に-1が入るので、Pos2に突入させない-->
    <Pos1>$(Tmp.IndexOf("."))</Pos1>
    <Pos1n>$([MsBuild]::Add($(Pos1), 1))</Pos1n>
    <Pos2 Condition="0 &lt;= $(Pos1)">$(Tmp.IndexOf(".", $([MsBuild]::Add($(Pos1), 1))))</Pos2>
    <Pos2n>$([MsBuild]::Add($(Pos2), 1))</Pos2n>
    
    <!-- 最初のピリオドがあれば、そこまでを VerMajor にする。ピリオドがなければ、全てを VerMajor にする。-->
    <VerMajor  Condition="0 &lt;= $(Pos1)">$(Tmp.SubString(0, $(Pos1)))</VerMajor>
    <VerMajor  Condition="0 &gt;  $(Pos1)">$(Tmp)</VerMajor>

    <!-- 次のピリオドがあれば、 最初のピリオドから次のピリオドまでを、VerMinorにする。なければ、残りをVerMinorにする-->
    <VerMinor  Condition="0 &lt;= $(Pos2)">$(Tmp.SubString($(Pos1n), $([MsBuild]::Subtract($(Pos2), $(Pos1n)))))</VerMinor>
    <VerMinor  Condition="0 &gt;  $(Pos2) And 0 != $(Pos1n)">$(Tmp.SubString($(Pos1n)))</VerMinor>
    
    <!--2つ目のピリオドがあれば、そこから後ろを VerBuild にする-->
    <VerBuild  Condition="0 &lt;= $(Pos2)">$(Tmp.SubString($(Pos2n)))</VerBuild>

    <!-- VerMajor,VerMinor,VerBuildを用いて、定数を作成する。 -->
    <DefineConstants>$(DefineConstants);NET_$(VerMajor)$(VerMinor)$(VerBuild)</DefineConstants>
    <DefineConstants Condition="2 &lt;= $(VerMajor)">$(DefineConstants);NET_20_OR_GREATER</DefineConstants>
    <DefineConstants Condition="3 &lt;= $(VerMajor)">$(DefineConstants);NET_30_OR_GREATER</DefineConstants>
    <DefineConstants Condition="3 &lt;= $(VerMajor) And 5 &lt;= $(VerMinor)">$(DefineConstants);NET_35_OR_GREATER</DefineConstants>
    <DefineConstants Condition="4 &lt;= $(VerMajor)">$(DefineConstants);NET_40_OR_GREATER</DefineConstants>
    <DefineConstants Condition="4 &lt;= $(VerMajor) And 5 &lt;= $(VerMinor)">$(DefineConstants);NET_45_OR_GREATER</DefineConstants>

  </PropertyGroup>

</Project>

csproj (MsBuild) のデバッグ

csprojファイルをいじるときに、他の人はどうやってデバッグしているのだろうか?

サンプルとして、TargetFrameworkVersionの先頭に"NET_"をつけて、TestPropertyに入れて、DefineConstantsに追加した。
  <None Include="App.config" />
  </ItemGroup>
  
  <PropertyGroup>
    <TestProperty>NET_$(TargetFrameworkVersion.Replace("v","").Replace(".",""))</TestProperty>
    <DefineConstants>$(DefineConstants);$(TestProperty)</DefineConstants>
  </PropertyGroup>
  
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <!-- To modify your build process, add your task inside one of the ta .... 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
  <Target Name="Build">
    <Message Text="テストプロパティ = $(TestProperty)" />
    <Message Text="ビルド定数       = $(DefineConstants)" />
  </Target>
</Project>

このように、Visual Studioのコマンドプロンプトから、MsBuildを実行すると、Messageタスクが出力される。


このNET_45というシンボルは、ちゃんとVisualStudioで使える。


今度はターゲットフレームワークをNet4.0にすると、シンボル名もNET_40になる。

当然、NET_45シンボルは無いので、#if で括った部分は無効になる。

2014.06.18追記
csprojで使えるメソッドとかの説明
http://msdn.microsoft.com/ja-jp/library/dd633440.aspx

2014年6月4日水曜日

Collection クラス

コレクションの雛形なんだけど、実はC# 2.0からあったんだね。

/// <summary>上限付きコレクション</summary>
public class LimitedCollection<T> : Collection<T>
{
    private int limit = int.MaxValue;

    public int Limit
    {
        get
        {
            return this.limit;
        }

        set
        {
            this.limit = value;
            this.CheckLimit();
        }
    }

    private void CheckLimit()
    {
        if (this.Limit <= this.Count)
        {
            throw new InvalidOperationException();
        }
    }

    protected override void SetItem(int index, T item)
    {
        this.CheckLimit();
        base.SetItem(index, item);
    }

    protected override void InsertItem(int index, T item)
    {
        this.CheckLimit();
        base.InsertItem(index, item);
    }
}

Visual Studio 2013の拡張機能

やっとVisual Studio 2013のExpressEditionを卒業したので、拡張機能を入れて遊んでいる。
ちなみにTechNetが9月で終わるので、Visual Studio Onlineで月々2000円を3ヶ月間使い、MSDNを申し込む予定。

まずは定番の、Productivity Power Tools 2013
ColumnGuidesは、Indent Guidesを使うのでOFF
Middle-click scrollingはなんだかわからないのでOFF
Organize VB ImportとStructure Visualizerは使わないのでOFF

あと、Align AssignmentsをONにするとは、エディタの設定を変更する。
これは、代入文とかのイコールを揃える機能なので、
エディタの設定を変えておかないと、せっかく揃えた部分が、
ソース整形により、元に戻ってしまう。

ほかには、こんなものを入れている。
  • Resource Translator
  • VSColorOutput
  • Indent Guides
  • Snippet Designer

ReSharperを更新するかはビミョーだな。
むしろ、OpenCover UIの出来によっては、
DotCoverを買うほうがいいかもね。

2014年5月26日月曜日

匿名型を返す

無理矢理に書いてみたが、

public class Foo
{
    public T Bar<T>(T valType)
    {
        return (T)Activator.CreateInstance(valType.GetType(), new object[] { "Bar", 17 });
    }
}

呼び出す側で、匿名型を作らなくてはならない。
static void Main(string[] args)
{
    var obj = new Foo();
    var result = obj.Bar(new { name = "", age = 0, });
    Console.WriteLine("name={0}, age={1}", result.name, result.age);

しかも、匿名型の型が違うとCreateInstanceで例外になるので(名前はOK)、
事前にチェックをしなくてはならない。
valType.GetType().GetConstructor(new Type[] { typeof(string), typeof(int) } );

もし、BarメソッドのGetConstructorで、エラーを検出したとして、どうする?
例外を投げる?・・・使いづらいな。

やはり、別途クラスを宣言するほうがよさそうだ。
Tupleも微妙に使いづらいんだよね。

2014年3月18日火曜日

2014年3月14日金曜日

windowsでRedmine (bitnami) バックアップとリストア

bitnami Redmienが2.5.0にバージョンアップしたので、実験用マシンをアップデートした。
  1. サービスの停止
    インスコフォルダのmanager-windows.exeで、MySQL以外を停止する。

  2. データベース名、ユーザ名、パスワードを調べる
    {インスコフォルダ}\apps\redmine\htdocs\config\database.yml
    production:
      adapter: mysql2
      database: bitnami_redmine
      host: 127.0.0.1
      username: bitnami     ← ユーザ名
      password: XXXXXXXX    ← パスワード
      encoding: utf8
    

  3. データベースのバックアップ
    インスコフォルダのuse_redmine.batでコマンドプロンプトを使う。
    $ mysqldump -u bitnami -pXXXXXXXX bitnami_redmine > d:\redmine_backup

  4. 添付ファイルのバックアップ
    {インストフォルダ}\apps\redmine\htdocs\filesをコピーする。

  5. サービスのアンインスコ
    MySQLを停止してから、serviceinstall.batを実行する。

  6. 念のため、現在のインスコフォルダをリネームしておく

  7. 新しいバージョンのインスコ

  8. サービスを止める
    インスコフォルダのmanager-windows.exeで、MySQL以外を停止する。

  9. データベースのリストア
    $ mysql -u bitnami -pXXXXXXXX bitnami_redmine < d:\redmine_backup
    ※再インスコしたらパスワードが変わっているので、手順2で確認すること!

  10. 添付ファイルのリストア
    手順4のフォルダを書き戻すだけ。

  11. サービスの起動
    すべてのサービスを起動する。