2014年4月25日

Visual Studio の CSS の警告がうざい場合の対処法

Visual Studio 2010 の場合、真っ当な対処法として、Web Standards Update for Microsoft Visual Studio 2010 SP1 をインストールし、css ファイルを開いた際のツールバーで CSS 3.0 を選択する。
※ただし、設定維持の動作がおかしく、aspx ファイルを開くと CSS 2.1 に戻されたりする

上記を行っても、いくつかの警告は残る。
  • @charset
  • 非対応プロパティ (ime-mode など)
  • 妥当でない CSS ハック

そこで、Visual Studio の設定により、CSS の警告を出さないようにすることができる。
※同時にプロパティの typo などを検出できなくなる点に注意
  1. メニューの [ツール] → [オプション] で、オプション画面を表示
  2. [テキスト エディター] → [CSS] → [その他]
  3. [エラーの検出]チェックを外す
    [CSS][その他]
  4. [OK]を押して設定を保存

2014年4月22日

ul > li > div でテーブルレイアウト

html + css で <table> を使わずにテーブルレイアウトを作る方法をググったところ、<div> のみで構築する方法と、<ul> <li> <div> で構築する方法が多かった。
<div> のみだと class が増えそうなので、<ul> <li> <div> で構築する方法を選択。

しかし、検索結果の情報には、なぜか <li> の下の <div> に「float:left;」を使ったものが多い。
「float:left;」の何が問題かというと、「vertical-align」と共存できない・・・

そこで、下記を使ってみた。
ul → table , li → tr , div → td のように使用可能。

ul.table-layout {
    list-style: none;
    display: table;
    margin: 0;
    padding: 0;
}
ul.table-layout > li {
    display: table-row;
}
ul.table-layout > li > div {
    display: table-cell;
    vertical-align: middle;
}

使用例

[HTML ソース]

<ul class="table-layout">
    <li>
        <div style="padding:5px">
            <div style="border:1px solid #ff00ff">て<br>す<br>と</div>
        </div>
        <div style="padding:5px">
            <div style="border:1px solid #ffff00">レイアウト</div>
        </div>
        <div style="padding:5px">
            <div style="border:1px solid #00ffff">で<br>す</div>
        </div>
    </li>
</ul>

[プレビュー]


  • レイアウト

備考

この方法は "dispaly: table" に対応してないブラウザでは不可。
つまり、IE7 等では諦めて <table> を使うのが無難。

2014年4月20日

IEnumerable.IsNullOrEmpty

string.IsNullOrEmpty の配列版。あると便利。
※ null で例外が欲しい場合は、Enumerable.Any() がある。


// generic 版
public static bool IsNullOrEmpty<T>(this IEnumerable<T> source)
{
    if( source == null ) return true;
    using( var e = source.GetEnumerator() ) return !e.MoveNext();
}

// 非 generic 版(普通は不要)
public static bool IsNullOrEmpty(this IEnumerable source)
{
    return source == null || !source.GetEnumerator().MoveNext();
}

ちなみに、string にも使用可能。(IEnumerable<char> なので)

おまけ

パフォーマンスを上げたい場合は、下記メソッドを別途定義する。
ICollection<T> を実装しているクラス(List<T> や配列)は、Count の参照だけで済むため、早くなる。

public static bool IsNullOrEmpty<T>(this ICollection<T> source)
{
    return source == null || source.Count == 0;
}

おまけのおまけ

下記パターンでパフォーマンスを測定してみた。
(A) 引数が IEnumerable<T>
(B) 引数が IEnumerable<T> と ICollection<T> (おまけのパターン)
(C) 引数が IEnumerable<T> で、内部で ICollection<T> にキャスト試行(下記実装)

public static bool IsNullOrEmpty<T>(this IEnumerable<T> source)
{
    if( source == null ) return true;
    var c = source as ICollection<T>;
    if( c != null ) return c.Count == 0;
    using( var e = source.GetEnumerator() ) return !e.MoveNext();
}

[測定コード]

static void Main(string[] args)
{
    var testdata = new[]{
        new int[]{},
        new[]{1}
    };

    var loopCount = int.Parse(args[0]);
    var result = false;
    var sw = new Stopwatch();
    sw.Start();
    for( var i=0; i<loopCount; i++ ){
        result = testdata[i % testdata.Length].IsNullOrEmpty();
    }
    sw.Stop();
    Console.WriteLine(sw.Elapsed);
    Console.WriteLine(result);
}

[結果]
loopCount (A) (B) (C)
10000 0.0012318 0.0004056 0.0056082
100000 0.0038177 0.0010864 0.0455488
※コマンドプロンプトで実施(csc /optimize)
※10回の算術平均、単位は[s]

結果は、(B) > (A) > (C) となった。(左の方が高性能)

(C) が遅い原因はキャスト試行処理なので、つまり、パフォーマンス目的でキャスト処理を追加する際は要注意。
(B) のようにキャストをオーバーロードに任せられるパターンは、そうすべき。(メソッドが増えるデメリットはあるけど)

検証環境

Windows 7 64bit/Visual Studio 2010 SP1/.NET 4.0
Intel(R) Celeron(R) CPU G530 @ 2.40GHz/DDR3 8GB(4GB x 2)

2014年4月18日

.NET に nullReplace() は不要

下記のような共通メソッドを作ったことはないだろうか。
特に Java 出身者なら・・・

/// <summary>
/// null の場合、replacement に変換する。
/// </summary>
public static T NullReplace<T>(this T source, T replacement)
{
    return source == null ? replacement : source;
}

.NET 2.0 からの ?? 演算子(null 合体演算子)の導入により、このメソッドは無用の長物になる。
(.NET 2.0 以降でないとこのメソッドは作れないが、それはスルーで)
?? 演算子は、null 許容型を便利に扱うため導入されたと思われるが、null 許容型だけでなく、普通の参照型にも使える。

// null 許容型
int? num = null;
int num2 = num ?? 0;
Console.WriteLine(num2); // 0

// null 許容型のみ GetValueOrDefault() でも同じことが可能
// ※メソッドなので、引数が必ず評価される点は注意
int num3 = num.GetValueOrDefault(0);
Console.WriteLine(num3); // 0

// 参照型
string text = null;
string text2 = text ?? "<null>"; // NullReplace() と同じ動作
Console.WriteLine(text2); // <null>

ちなみに VB では、2 引数の If 演算子を使う。
※これが使えるのは Visual Studio 2008 から

Dim text As String = Nothing
Dim text2 As String = If(text, "<null>") ' NullReplace() と同じ動作
Console.WriteLine(text2) ' <null>

というわけで、NullReplace() の導入はやめよう、という話。

2014年4月17日

.NET バージョン毎にプロジェクトを分けてソースを共有する

たまに、複数の .NET バージョンでモノが欲しい時がある。
その際、複数のプロジェクトファイル(.NET バージョン違い)に同じソース群を参照させる。
その手順のメモ。

※元となるプロジェクトファイルは「MyLibrary.csproj」、バージョンは .NET 4.0 とする。
  1. プロジェクトファイル(csproj/vbproj)を複製
    MyLibrary.csproj → MyLibrary_net35.csproj
  2. バージョン変更する方のプロジェクトファイルの /Project/PropertyGroup/TargetFrameworkVersion を変更
    ・MyLibrary_net35.csproj
    
    <Project ToolsVersion="4.0" DefaultTargets="Build"
             xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup>
        <!-- 中略 -->
        <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    
  3. 両方のプロジェクトファイルに /Project/PropertyGroup/BaseIntermediateOutputPath + BaseOutputPath を追加し、/Project/PropertyGroup[@Condition]/OutputPath を変更
    (中間ファイルとバイナリの出力先の変更)
    ・MyLibrary.csproj
    
    <Project ToolsVersion="4.0" DefaultTargets="Build"
             xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup>
        <BaseIntermediateOutputPath>obj\net40\</BaseIntermediateOutputPath>
        <BaseOutputPath>bin\net40\</BaseOutputPath>
    
    ・MyLibrary_net35.csproj
    
    <Project ToolsVersion="4.0" DefaultTargets="Build"
             xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup>
        <BaseIntermediateOutputPath>obj\net35\</BaseIntermediateOutputPath>
        <BaseOutputPath>bin\net35\</BaseOutputPath>
    
    ・両方
    
    <Project ToolsVersion="4.0" DefaultTargets="Build"
             xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <!-- 中略 -->
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == '{構成設定}' "">
        <!-- 中略 -->
        <!-- 必要に応じて $(Platform) もパスに組み込む(マルチプラットフォーム等) -->
        <OutputPath>$(BaseOutputPath)\$(Configuration)\</OutputPath>
    
  4. Visual Studio で変更したプロジェクトファイルを再読込
  5. Visual Studio で複製したプロジェクトファイルを追加読込
    ソリューションを右クリック → [追加] → [既存のプロジェクト]
  6. .NET のバージョンに合わせたコード修正

備考

バイナリ出力先を分けるのは当然として、中間ファイル出力先を分けておかないと、Visual Studio の挙動がおかしくなることがある。リビルドでないと、ビルド成功しなかったり・・・
※複数プロジェクトで中間ファイル出力先がかぶることは、Visual Studio でビルド失敗する原因の 1 つ

中間ファイル出力先は、IntermediateOutputPath で構成設定毎に指定することも可能。
ただし、中間ファイル出力先を変更しても、「obj\Debug」「obj\Release」などのフォルダは作られる・・・

参考URL

2014年4月14日

Dictionary.TryGetValue のすゝめ

※ Dictionary について熟知している人は読み飛ばし推奨

Dictionary.TryGetValue というメソッドがある。
初見だと「何のためにあるの?」と疑問を抱く人は多い・・・はず。
処理内容が インデクサ(Item プロパティ) とかぶっているため、使う必要性を感じられずに無視してる人もいると思う。

結論から言うと、このメソッドは key の存在を確認してから value を取り出すパターンにおいて、非常に有効である。
  • ContainsKey + インデクサ

var dic = new Dictionary<string, int>();

// ---- key 追加処理 ----

if( dic.ContainsKey("ススメーススメー") ){
    Console.WriteLine(dic["ススメーススメー"]);
}
  • TryGetValue

var dic = new Dictionary<string, int>();

// ---- key 追加処理 ----

int val;
if( dic.TryGetValue("ススメーススメー", out val) ){
    Console.WriteLine(val);
}

上記の差は、TryGetValue 使用時の方が、key ルックアップが 1 回少なくなる点である。
key ルックアップはあまり早い処理ではないため、処理回数は少ないほどいい。

2014/09/26 修正:
Dictionary のソースを見て MSDN を読み間違えてたことに気付きました。
読み取り処理だけならスレッドセーフ、変更処理を含めるとスレッドセーフではない。

パフォーマンス

[測定コード]

static void Main(string[] args)
{
    // データの準備
    var dic = new Dictionary<string, int>();
    var key = "";
    for( var i=0; i<10; i++ ){
        key += "ススメー";
        dic.Add(key, i + 1);
    }
    int loopCount = 10000;
    var temp = new List<int>(loopCount);
    var flag = int.Parse(args[0]);
    var searchKey = args[1];

    // 測定実施
    var sw = new Stopwatch();
    sw.Start();
    switch( flag ){
    case 1:
        // ContainsKey + インデクサ
        for( var i=0; i<loopCount; i++ ){
            if( dic.ContainsKey(searchKey) ) temp.Add(dic[searchKey]);
        }
        break;
    case 2:
        // TryGetValue
        int ret;
        for( var i=0; i<loopCount; i++ ){
            if( dic.TryGetValue(searchKey, out ret) ) temp.Add(ret);
        }
        break;
    }
    sw.Stop();
    Console.WriteLine(sw.Elapsed);
}

[結果]
searchKey ContainsKey
+ インデクサ
TryGetValue
存在するキー ススメーススメー 0.0011107 0.0006471
存在しないキー ススメ 0.0002466 0.0002814
※コマンドプロンプトで実施(csc /optimize)
※10回の算術平均、単位は[s]

存在するキーの場合は、ルックアップが 1 回少ない TryGetValue の圧勝。
存在しないキーの場合は、ContainsKey と TryGetValue の速度差になる。

検証環境

Windows 7 64bit/Visual Studio 2010 SP1/.NET 4.0
Intel(R) Celeron(R) CPU G530 @ 2.40GHz/DDR3 8GB(4GB x 2)

参考URL

2014年4月11日

Visual Studio の VB.NET 開発で、最初にやって欲しいこと

最初に 1 回だけ設定すると、幸せになれる。(個人差あり)
  1. Visual Studio を起動する。
  2. メニューの [ツール] → [オプション] で、オプション画面を表示
  3. [プロジェクトおよびソリューション] → [Visual Basic の規定値] を選択
  4. 画像のように設定
    ※デフォルトは Option Strict「Off」
    [Visual Basic の規定値]
  5. [OK]を押して設定を保存

効果

VB.NET プロジェクトを新規作成する際、プロジェクトプロパティのデフォルトが上記となる。
これで、毎回 Option Strict「On」にする必要も、変更し忘れる心配も無し♪♪

※本稿は VS 2010 での話だが、VS 2013 でもデフォルトは Option Strict「Off」のまま・・・たぶん今後も変わらないのだろう

2014年4月10日

C#.NET 複数行の文字列リテラルの改行コード

C#.NET は、@"" 形式だと文字列リテラルを複数行に書ける。
その場合の改行コードについて検索してみたが、見つからなかったので検証。(ググり方が下手なだけかも・・・)

[検証コード]

using System;

static class Program
{
    static void Main()
    {
        var text = @"minami
kotori";
        Console.WriteLine(text.Replace("\r", "[CR]").Replace("\n", "[LF]"));
    }
}

[結果]
ソースの
改行コード
結果
CRLF minami[CR][LF]kotori
CR minami[CR]kotori
LF minami[LF]kotori

複数行の文字列リテラルの改行コードは、ソースの改行コードと同じになる。
Visual Studio 使ってれば問題ないだろうけど、低機能のテキストエディタで改行コードごちゃまぜだったりすると・・・

検証環境

Windows 7 64bit/Visual Studio 2010 SP1/.NET 4.0

2014年4月9日

VB.NET の Object 型からは拡張メソッド使用不可

Object 型から拡張メソッドを使うことは滅多にないだろうけど、たまたまそのようなコードを書いて、拡張メソッドが使えなくてビビったことがある。
C#.NET にこの問題はなく、VB.NET 固有の問題になる。

  • 拡張メソッド

public static class Extensions
{
    public static int GetHashCodeSafely<T>(this T source)
    {
        return source == null ? 0 : source.GetHashCode();
    }
}
  • C#.NET から利用

var num = 1;

// 問題無し
var hash = num.GetHashCodeSafely();
// 問題無し
hash = ((object)num).GetHashCodeSafely();
  • VB.NET から利用

Dim num = 1

' 問題無し
Dim hash = num.GetHashCodeSafely()
' コンパイル不可「Option Strict On では、遅延バインディングを使用できません。」
hash = DirectCast(num, Object).GetHashCodeSafely()

遅延バインディングとは、VB.NET の機能で「Option Strict Off」時に Object 型に対してその定義にないメソッドやプロパティを呼べること。
.NET 4.0 からの dynamic 型と同じようなことが、VB.NET では Object 型に対して可能。

Option Strict Off

Dim text As String = ""
Dim obj As Object = text
Console.WriteLine(obj.Length)  ' 0 が出力される
Console.WriteLine(obj.Lengths) ' Lengths メンバは存在しないため、実行時エラー

つまり、VB.NET で Object 型から生えてるものは、自身のメンバを除いてすべて遅延バインディングと判断される。
そのため、呼び出し方法がかぶる拡張メソッドは使えない。(初期の VB.NET 開発時に拡張メソッドなんて想定してないだろうし・・・)
「Option Strict On」の時のみ Object 型に対する拡張メソッドを有効にできれば理想だけど、たぶんコンパイラの修正が難しいのだろう。

今後、この問題が改良される可能性は低いだろうから、Object 型に対しては、あきらめて普通の静的メソッドを使うしかない。

検証環境

Windows 7 64bit/Visual Studio 2010 SP1/.NET 4.0

参考URL

2014年4月8日

VB の Module ≒ C# の static class

Java/C# ユーザーが初めて VB.NET に触れた時、「Module(モジュール)って何?」という疑問にぶち当たる確率は高いと思う。
簡単に言ってしまえば、静的メンバのみのクラス(C# の static class)で、呼び出し側でクラス名(モジュール名)を省略できる特徴がある。
文字だと分かりにくいのでコードで

Public Module ModuleSample
    Sub New()
        ' コンストラクタも定義できる(静的コンストラクタに該当)
        Console.WriteLine("ModuleSample.New")
    End Sub

    Public Sub TestCall()
        ' Shared が無いけど、静的メソッド(共有メソッド)
        Console.WriteLine("ModuleSample.TestCall")
    End Sub
End Module

Module EntryPoint
    ' モジュール/クラスの Main メソッドはプログラムのエントリポイント
    Sub Main()
        ' 呼び出し時
        TestCall()              'モジュール名を省略できる
        ModuleSample.TestCall() '省略しないことも可能
    End Sub
End Module

'[結果]
' ModuleSample.New
' ModuleSample.TestCall
' ModuleSample.TestCall

C# からは、static class として扱える。

static class EntryPoint
{
    static void Main()
    {
        ModuleSample.TestCall();
    }
}

//[結果]
// ModuleSample.New
// ModuleSample.TestCall

Module の実態は static class とほぼ同じなのだが、C# の static class を VB から呼ぶ場合は Module のようにクラス名を省略できない。その違いは、StandardModuleAttribute である。
この属性を C# の static class に付けてやると、VB から呼ぶ場合にクラス名を省略できる。
ただ、MSDN の説明に「独自に作成したコードから直接使用するためのものではありません。」「これは、コードから直接呼び出すためのものではありません。」とあり、ユーザーコードからは使って欲しくない模様・・・

using System;
using Microsoft.VisualBasic.CompilerServices;

[StandardModule]
public static class CShapModuleSample
{
    public static void TestCall()
    {
        Console.WriteLine("CShapModuleSample.TestCall");
    }
}

MSIL の比較

VB - Module .class public auto ansi sealed
C# - static class .class public abstract auto ansi sealed beforefieldinit
static class には、abstract/beforefieldinit が付いている。
static class の場合、beforefieldinit は静的コンストラクタがあると付かないが、Module の場合、コンストラクタの有無に関わらず付かない。
つまり、Module のフィールドは必ず最初のアクセス時に初期化される。(アクセスがなければ初期化されない)

拡張メソッド

VB の拡張メソッドは Module でしか作れない。

Imports System.Runtime.CompilerServices

Public Module Extensions
    <Extension()>
    Public Function IsNotNull(Of T)(ByVal source As T) As Boolean
        Return source IsNot Nothing
    End Function
End Module

' 呼び出し方法は 3 通り可能・・・
Dim val = ""
val.IsNotNull()
IsNotNull(val)
Extensions.IsNotNull(val)

まとめ

Module は多用しない方がいい。
クラス名(モジュール名)を省略することは、名前の衝突確率を上げることになり、あまりよくない。
プログラムのエントリポイント、拡張メソッド以外での利用は避けた方がいい。

どうしても使いたい場合は、アクセス修飾子を Friend にする、専用の名前空間にする、などの条件を設けるべき。
余談だが、拡張メソッドも名前の衝突確率が高いので、専用の名前空間を推奨。

VB は Module があるためか、静的クラスが無いので、代わりに下記のような疑似静的クラスを使ったりする。

' NotInheritable で継承禁止
' Private コンストラクタでインスタンス生成を禁止
Public NotInheritable Class StaticClass
    Private Sub New()
    End Sub

    ' ---- 以降、Shared メンバを定義 ----
End Class

検証環境

Windows 7 64bit/Visual Studio 2010 SP1/.NET 4.0

2014年4月7日

C#.NET でインデクサの名前変更

C#.NET のインデクサのデフォルトの名前は Item である。
機会は少ないが、下記のように Item を別のメンバの名前にする場合、そのままではコンパイルが通らない。

public class IndexerName
{
    // コンパイルエラーになる
    public string this[int index] { get { return index.ToString(); } }

    public string Item;
}

そこで、IndexerNameAttribute で、インデクサの名前を変更することができる。

using System.Runtime.CompilerServices;

public class IndexerName
{
    // インデクサの名前が"Indexer"になり、コンパイルエラーにならない。
    // ※変数名の禁則文字は使えない。(エラーになる)
    // ※VB からなら instance(0) の他に、instance.Indexer(0) でもアクセス可能
    [IndexerName("Indexer")]
    public string this[int index] { get { return index.ToString(); } }

    public string Item;
}

参考URL

2014年4月4日

プロパティの比較

項目 C#.NET VB.NET
静的プロパティ
読込専用プロパティ
書込専用プロパティ
get, set 毎のアクセス修飾子
引数付きプロパティ
インデクサ
自動プロパティ
自動プロパティの
get, set 毎のアクセス修飾子
自動プロパティの初期化
自動プロパティの
バッキングフィールド
<PropertyName>k__BackingField _PropertyName
※VB.NET の get, set 毎のアクセス修飾子は、Visual Studio 2008/2010 で可能、2003 では不可であることを確認。
MSDN を信じると Visual Studio 2005 から可能。

  • C#.NET

using System;
using System.Reflection;

class CSharpProperties
{
    // 静的プロパティ
    public static int SValue
    {
        get { return -1; }
        set { Console.WriteLine(value); }
    }

    // 読取専用プロパティ
    public int RValue { get { return 0; } }

    // 書込専用プロパティ
    public int WValue { set { Console.WriteLine(value); } }

    // get, set 毎のアクセス修飾子
    public int RWValue
    {
        get { return 0; } // 省略時はプロパティと同じ = public
        private set { Console.WriteLine(value); }
    }

    // 引数付きプロパティ -> 不可
    // public string ParamValue[int index] { get { return index.ToString(); } set { } }

    // インデクサ
    public string this[int index]
    {
        get { return index.ToString(); }
        set { Console.WriteLine("[{0}] = {1}", index, value); }
    }

    // 自動プロパティ
    public int AutoValue { get; set; }

    // 自動プロパティの get, set 毎のアクセス修飾子
    public int RAutoValue { get; private set; } // 外部から読取専用
    public int WAutoValue { private get; set; } // 外部から書込専用

    // 自動プロパティの初期化 -> 不可
    // public int IAutoValue { get; set; } = 1;

    // 自動プロパティのバッキングフィールドへのアクセス -> リフレクションで可能
    public void AccessToAutoValue()
    {
        var fi = this.GetType().GetField("<AutoValue>k__BackingField",
                                         BindingFlags.Instance | BindingFlags.NonPublic);
        Console.WriteLine(fi.GetValue(this));
    }
}

  • VB.NET

Imports System

Class VbProperties
    ' 静的プロパティ
    Public Shared Property SValue As Integer
        Get
            Return -1
        End Get
        Set(value As Integer)
            Console.WriteLine(value)
        End Set
    End Property

    ' 読取専用プロパティ
    Public ReadOnly Property RValue As Integer
        Get
            Return 0
        End Get
    End Property

    ' 書込専用プロパティ
    Public WriteOnly Property WValue As Integer
        Set(value As Integer)
            Console.WriteLine(value)
        End Set
    End Property

    ' get, set 毎のアクセス修飾子
    Public Property RWValue As Integer
        Get
            Return 0
        End Get
        Private Set(value As Integer)
            Console.WriteLine(value)
        End Set
    End Property

   ' 引数付きプロパティ
    Public Property ParamValue(ByVal index As Integer) As String
        Get
            Return index.ToString()
        End Get
        Set(value As String)
            Console.WriteLine("[{0}] = {1}", index, value)
        End Set
    End Property

    ' インデクサ
    Default Public Property Item(ByVal index As Integer) As String
        Get
            Return index.ToString()
        End Get
        Set(value As String)
            Console.WriteLine("[{0}] = {1}", index, value)
        End Set
    End Property

    ' 自動プロパティ
    Public Property AutoValue As Integer

    ' 自動プロパティの get, set 毎のアクセス修飾子 -> 不可
    ' Public Property RAutoValue As Integer
    '     Get
    '     Private Set
    ' End Property

    ' 自動プロパティの初期化
    Public Property IAutoValue As Integer = 1

    ' 自動プロパティのバッキングフィールドへのアクセス -> 直に可能
    Public Sub AccessToAutoValue()
        Console.WriteLine(_AutoValue)
    End Sub
End Class

まとめ(感想のようなもの)

get, set 毎のアクセス修飾子を設定できる C# のプロパティは、とても便利。
しかもインデクサ、自動プロパティでも可能!!
C# は、自動プロパティで get, set 毎のアクセス修飾子を設定できる点が便利。
ただ、インデクサ以外の引数付きプロパティは使えない・・・残念。

対する VB の利点は、引数付きプロパティが使えること、自動プロパティが初期化できること。
欠点としては、バッキングフィールドに簡単にアクセスできること。
バッキングフィールドと同じ名前のメンバが定義できず、地味に不便。

おまけ

C# から VB の引数付きプロパティへのアクセスは下記で可能。
・・・ダサい。

var instance = new VbProperties();
// getter
var temp = instance.get_ParamValue(1);
// setter
instance.set_ParamValue(1, "a");

検証環境

Windows 7 64bit/Visual Studio 2010 SP1/.NET 4.0

参考URL

2014年4月2日

VB.NET に ByOut は無いが OutAttribute はある

VB.NET に C#.NET の out に相当するものは、残念ながら存在しない。

VB.NET だけなら ByRef を使用していればいいのだが(使用するしかない)、VB.NET で作成した ByRef 引数を持つメソッドを C#.NET から呼ぶ場合、ref / out が区別できないと不便である。(ByRef のみでは ref に相当する)
これを解決する方法として、OutAttribute がある。


' C# から呼ぶ場合、引数"ret"は ref として扱える。
Public Shared Function AddRef(ByVal x As String, ByVal y As String,
                              ByRef ret As Integer) As Boolean
    Dim xi, yi As Integer
    If Integer.TryParse(x, xi) AndAlso Integer.TryParse(y, yi) Then
        ret = xi + yi
        Return True
    End If
    Return False
End Function

' C# から呼ぶ場合、引数"ret"は out として扱える。
Public Shared Function AddOut(ByVal x As String, ByVal y As String,
                              <Out()> ByRef ret As Integer) As Boolean
    Dim xi, yi As Integer
    If Integer.TryParse(x, xi) AndAlso Integer.TryParse(y, yi) Then
        ret = xi + yi
        Return True
    End If
    Return False
End Function

ただし、この属性は、C#.NET から呼ぶ場合に out として扱えるだけで、VB.NET に out 機能を提供するわけではないことに注意。
あくまで VB.NET は、ref = ByRef のみ。