SharpDevelop で、VB.Net を C# に変換してみた( その2 )

# 背景

[前回](https://qiita.com/wacha/items/64df18569b5b8a5c5bd6)やったことの続きです。

ShartDevelop を使って、VB.Net のソースを C# に移行しようというプロジェクトの日記にしようと思います。

さて、前回でごく基本的な文法の変換は完了してエラー無く移行ができましたが、まだまだ難敵がいました。

1. 拡張メソッド
1. グローバル関数
1. インデックス付プロパティ


まだまだ新たな敵が出てくるかもしれませんが、とりあえずこいつらをやっつけていきます。

# 拡張メソッド

前回のでスライムたちを倒して、レベルアップした気になってますが、拡張メソッドは手修正が必要なだけで、おそらく四天王の中でも最弱です。

基本的には、次のページを参照。

https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.compilerservices.extensionattribute?view=net-5.0

この注釈部分がちょっと分かりにくいですが、注釈にある一つ目の「[拡張メソッド](https://docs.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/language-features/procedures/extension-methods)」のリンク先は、VB の拡張メソッド。二つ目の「[拡張メソッド](https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/extension-methods)」は、C# の拡張メソッドの説明なのでそれぞれ確認。（さらに、this が、「この」に翻訳されているのがさらに難解にさせているという💦）

```VB:Before
<Extention>
Public Function Hoge( value As String ) As Integer
    Return HogeInternal( value )
End Function
```

```C#:After
public static int Hoge( this String value )
//                      ↑ ここに this を追加
{
    return HogeInternal( value );
}
```

基本的には、Extention 属性を削除して、関数の第一引数に this をつけてあげればよさそう。

また、他の VB で作成した dll にある Extentions 属性で作成した拡張メソッドは、そのまま C# で使えたのでそこは一安心。


# グローバル関数
これは、まだはっきりしてませんが、c# にはグローバル関数（クラス名や名前空間を省略しての関数呼び出し）ができないのではないでしょうか？

つまり、VB だとこんな感じでした。

```VB:Before
Module Module1
    Public Function Hoge( value As String ) As Integer
        Return Fuga( value )
    End Function
End Module

Public Class Foo
    Public Sub Bar( value As String )
        Dim x = Hoge( value )
    End Sub
End Function
```

つまり、Module で宣言した関数はクラス名等を指定せず使用できました。
これが、C# だとできない気がして、下記のようになってます。

```C#:After
static class Module1 {
    public static int Hoge( String value )
    {
        return Fuga( value );
    }
}

class Foo {
    public void Bar( String value )
    {
        var x = Module1.Hoge( value );
        //      ↑ Module1 を明示的に指定
    }
}
```

これって、追加する作業も面倒ですが、VB に慣れてると今後のメンテナンスが面倒だなぁと思ってしまいました。
(追記)呼び出し側のソースの最初に、`using static Project1.Module1`みたいに書いてあげればいけました💦もうちょっと詳しくは、「[VB.Net から移行した人のための C# Tips](https://qiita.com/wacha/items/c96ebccb8f7650cf7151)」に記載しました。


#インデックス付プロパティ

最後に、インデックス付プロパティ。
これが、VB から C# への変換のラスボスだと信じたい。

VB では、下記のようにプロパティにインデックスを使えます。

```VB
Public Property Hoge( index As Integer ) As String
```

これが、ググるとすぐに分かりますが C# には無い💦
インデクサというのが代わりに使えるっぽいので調べてみると、VB でいう Default Property がインデクサ相当ですね。

例えば、Dictionary クラスの Item なんかも、C# ではインデクサとして実装されていて、Item という名称のプロパティが無いという。

つまり、

```VB
Dim value = dic.Item( key )
Dim value = dic( key )
' ↑ どちらも OK
```

ですが、

```C#
var value = dic.Item( key )
// ↑ そんなプロパティは無い
var value = dic[ key ]
// ↑ インデックスとしてアクセスで OK
```

となります。

比較的、[このリンク](https://github.com/ufcpp/UfcppSample/issues/177)がその辺りを理解しやすい気がします。

びっくりしたのですが、.Net の SortedDictionary の[ソース](https://referencesource.microsoft.com/#System/compmod/system/collections/generic/sorteddictionary.cs,1a01ea5555bded49
)を見たら、Item なんてプロパティはどこにもなくて 260 行目あたりにインデクサの宣言があるだけなんですね。

というわけで、Item のようなプロパティだけだったら、それを Default Property にしてあげれば OK。

引数が、Integer だったら全部の値を取得して、配列を返してあげるようにするのが候補の一つ。ただ代入もできるようにすると面倒。

代入もできるようにするには、delegate を引数に取る専用クラスを使うか、ref 変数を戻す関数に変換してあげればできそうな気がするけど、まだ検証中です。

メンテナンス性が微妙ですが、とりあえずは get_Item, set_Item とかに書き換えるのがよさそう。

```VB:Before
Dim x = y.Prop( z )
y.Prop( z ) = x
```

を移行するとこんな感じ。

```C#:After
var x = y.get_Prop( z )
y.set_Prop( z, x )
```

プロパティを関数呼び出しすればいいんでしょうけど、イマイチ感が否めないですね😖

まだ前途多難な感じです。

その3に続くかも !?