佐々木屋

技術的なことから趣味まで色々書きます

似ているようで違うC#とVB.NET

私はC#VB.NETどちらでもプログラミング出来ますが、片方しか知らない方向けに違いをいくつか紹介していきたいと思います。

そもそも親戚?他人?

C#VB.NETはどちらもWindows系アプリケーション(ASP.NET)を作成できるプログラミングとして有名です。.NET Framework上で動作し、開発ツールはVisualStudioを使うところも一緒、同じオブジェクト指向型の言語です。

ここまでくると、ほとんど一緒?と思われますが、実は結構違います。
性格の違いをいくつか出してみます。

項目 C#
VB.NET
出身 C系言語(C、C++Javaなど)からの派生
言語機能としてはDelphiの影響も大きく受けている
VB6の後継として登場
互換性 CやC++との互換性はない VB6と互換性はないが同じ記法が可能
言語構成 C++Javaに近い
形式言語に近い
自然言語に近い
難易度 やや難しい
C++に比べるとはるかに簡単
簡単
VB6、VBAよりは難しい
文法規制 厳しい ゆるい

VB.NETが有利(C#が不利)なこと

  • 自然言語に近いので、初学者でも分かりやすい
  • VB6やVBAからの派生なので、マクロがある程度組めればVB.NETも理解できる
  • 文法規制がゆるいので多少間違えても正しく動作する
  • オブジェクト指向を理解する必要がないため、習得時間が短い
  • .NET Frameworkのクラスの他にVB独自の関数が使える(C#でも使用可だがひと手間必要)


C#が有利(VB.NETが不利)なこと

  • 文法規制が厳しいので、逆にWEBアプリケーションやdllなどのクラスライブラリ作成に向いている
  • ネットや書籍などの資料が非常に豊富
  • 演算子やキーワードがシンプル
  • コードが全体的に短い(VB.NETの2/3くらいで済む)
  • VB.NETにない機能(null演算子やインクリメントなど)が多い


誤解

× VB.NETよりC#の方が速度が早い
.NET Framework上で動作するので速度は一緒。

× C#はポインタが使えるがVB.NETでは使えない
そもそも.NET Frameworkはガベージコレクタが動いているので、C#でポインタを使うのが間違い。

× C#に比べてVB.NETの方が劣る
そもそも言語自体に優劣はない。
こういう風潮になった背景として、VB6やVBAなどが少し触れる「なんちゃってプログラマ」がC#に比べて圧倒的に多いことと、言語自体が覚えやすい、分かりやすく敷居が低いので、一般的にプログラマレベルが低いことが誤解を生んでいると思われる。


これからちょっとずつ機能の違いを紹介していこうと思います。

json.net バージョン不整合によるアプリケーションのサーバーエラー

ASP.NETのNuGetエラーの続きです。
どうもCodeBehindでWEBアプリケーションを作成してディレクトリごとコピーしてWEBサイトを発行すると、以下のメッセージが表示されてうまくコンパイルされません。

ファイルまたはアセンブリ 'Newtonsoft.Json'またはその依存関係の1つを読み込めませんでした。 見つかったアセンブリマニフェスト定義がアセンブリ参照と一致しません。 (HRESULTからの例外:0x80131040)

System.IO.FileLoadException:ファイルまたはアセンブリ 'Newtonsoft.Json、バージョン= 11.0.0.0、Culture = neutral、PublicKeyToken = 30ad4fe6b2a6aeed'またはその依存関係の1つを読み込めませんでした。 見つかったアセンブリマニフェスト定義がアセンブリ参照と一致しません。 (HRESULTからの例外:0x80131040)

よくよく読んでみると、どうもNewtonsoft.Json.dllのバージョンが違う?
確かにbinフォルダにあるNewtonsoft.Json.dllを見ると、バージョンが6.0になっている。エラー画面上は11.0だから全然違う。

とりあえず問題無いプロジェクトから正しいバージョンをコピーして実行するも、ビルド時にNuGetの自動更新機能が働いてしまい、また古いバージョンがダウンロードされてしまう・・・。


またまた路頭に迷いましたが、結局NuGetの自動更新ではなく、手動で更新しなければならいことが分かり、以下のコマンドをNuGetパッケージマネージャコンソールより実行し、プロジェクトを再起動したら直りました。

update-package Newtonsoft.Json -reinstall

Dictionaryクラスのソート

ジェネリックコレクションの一つであるDictionaryクラスのソート方法です。
Dictionaryクラスも配列なので、できる方法がたくさんありすぎて困るくらいです。ここでは私がよく使う3つの手法を紹介します。この方法以外でもいっぱいあるので、是非チャレンジしてみて下さい。

以下のDictionaryを考えます。

Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "hoge");
dic.Add(3, "piyo");
dic.Add(2, "fuga");

ソート結果が以下のようになることが目標。

1:hoge
2:fuga
3:piyo


Array.Sortメソッドを利用

多分今回紹介する中で一番高速に処理されます。DictionaryのKeyを一旦配列に変換してソートする方法です。

int[] arr = dic.Keys.ToArray();
Array.Sort(arr);
foreach(int i in arr) {
    Console.WriteLine("{0}:{1}", i.ToString(), dic[i].ToString());
}
Dim arr As Integer() = dic.Keys.ToArray()
Array.Sort(arr)
For Each i As Integer In arr
    Console.WriteLine("{0}:{1}", i.ToString(), dic(i).ToString())
Next


List.Sortメソッドを利用

DictionaryのKeyを一旦Listに変換してソートする方法です。arrayと大して変わりませんが、速度はこちらの方が遅いです。

List<int> lst = dic.Keys.ToList();
lst.Sort();
foreach (int i in lst) {
    Console.WriteLine("{0}:{1}", i.ToString(), dic[i].ToString());
}
Dim lst As List(Of Integer) = dic.Keys.ToList()
lst.Sort()
For Each i As Integer In lst
    Console.WriteLine("{0}:{1}", i.ToString(), dic(i).ToString())
Next

見た目はArrayにする方法と何ら変わりませんが、ListにすることでForeachメソッドが使用できたりとListの恩恵を受けたいときはこちらの方がいいと思います。

LINQのOrderByメソッドを利用

OrderByメソッドでソートした配列を型推論で一時的にコピーして利用する方法です。今回の中で一番遅い処理となりますが、LINQラムダ式を日常的に使用しているなら感覚的に一番簡単だと思います。私がやるなら(さほど大きなデータでなければ)これです。

var sdic = dic.OrderBy((x) => x.Key);
foreach (var v in sdic) {
    Console.WriteLine("{0}:{1}", v.Key, v.Value);
}
Dim sdic = dic.OrderBy(Function(x) x.Key)
For Each v In sdic
    Console.WriteLine("{0}:{1}", v.Key, v.Value)
Next

ASP.NETプロジェクトで謎のビルドエラー

何がどうなってこうなった?のか分かりませんが、会社で作成したASP.NET開発物がビルド(当然リビルド、発行もNG)出来なくなって、少し途方に暮れた時の話です。

  • VisualStudioの更新を当てた後におかしくなった?
  • でも問題ないプロジェクトもある

ビルドすると、以下のエラーが発生します。

f:id:sasaki816:20181227172958j:plain
「NuGetパッケージの復元」とか言っているので、NuGetの復元を試みます。
ツールNuGetパッケージマネージャパッケージマネージャ設定
の順にたどって開きます。
f:id:sasaki816:20190123234046j:plain
NuGetパッケージマネージャの設定
パッケージの復元欄の2つの項目にチェックが入っていれば、自動でNuGetを復旧してくれますが、チェックが入っていました。ということは、NuGetは関係ない?

散々路頭に迷った挙句、何とか直りました。結論から言えば、プロジェクトファイルのゴミを削除する必要がありました。以前も何かで修正したような。備忘として残しておきます。

 1.念のためプロジェクトをフォルダごとコピーしてバックアップ
 2.VisualStudioを終了
 3.プロジェクトファイル(.csproj, .vbproj)内の<TARGET>から</TARGET>までを削除
 4.再度VisualStudioでプロジェクトを読み込む

f:id:sasaki816:20181227173357j:plain
csproj

フォームの表示切替(補足)

前回メインフォームの表示切替の話をしましたが、少し説明というかイメージが足りませんでしたので補足します。
sasaki816.hatenablog.com


ここで説明されたメインフォームの入替で、ApplicationContextクラスのMainFormプロパティに設定したフォームがアプリケーション側が管理するフォームになります。
つまり、エントリポイントでApplication.Runメソッドで指定したフォームがForm1として、その後開いたフォームをForm2、Form3、Form4とすると以下のような図になります。

f:id:sasaki816:20190123101624j:plain
Form1がメインフォーム
当然この状態でForm1を閉じるとエントリポイントに処理が戻ってきてアプリケーションが終了します。その他のフォームを閉じても全く影響はありません。


ここで、Form2を開く際にApplicationContextクラスのメインフォームを入れ替えると、以下の図のように変化します。
f:id:sasaki816:20190123101822j:plain
Form2がメインフォーム
よってForm2を閉じるとエントリポイントに処理が戻ってきてアプリケーションが終了することになります。当然Form1を閉じてもアプリケーションが終了することはありません。

もしこれを全てVB6のような従来の方法(HideメソッドとShowDialog(Show)メソッド)で実現しようとすると、全てのフォームクローズイベント等でフォームの表示切替処理や管理処理を入れなければならず、またShowDialogの場合は開いたフォームのインスタンス破棄が必要になる場合があり、とても面倒なことになります。

クラスを考える⑥(呼び出し)

自作クラスを作ったわけですが、通常クラスの場合と静的クラスの場合で呼び出し方が少し異なります。

通常のクラス

通常のクラスはそのクラス専用にインスタンスを作成する必要がある為、newを使います。
以前作成したたい焼きクラスを使ってみます。

public class たい焼き {  
    public string 中身 { get; set; } = "あんこ";
    public int 焼く時間 { get; set; } = 5;

    public bool 焼きます() {
        try {
            //中身、焼く時間を使って焼く処理を記述
            return true;
        }
        catch {
            return false;
        }
    }
}
Public Class たい焼き
    Public Property 中身 As String = "あんこ"
    Public Property 焼く時間 As Integer = 5

    Public Function 焼きます() As Boolean
        Try
            '中身、焼く時間を使って焼く処理を記述
            Return True
        Catch
            Return False
        End Try
    End Function
End Class



クラス名に対してnewすればインスタンスを作成することが可能です。
その後はそのインスタンスを使用すればクラスメンバーやメソッドへアクセスできます。

public static void Main() {
    たい焼き taiyaki = new たい焼き();
    taiyaki.中身 = "クリーム";
    if (taiyaki.焼きます()) {
        Console.WriteLine("成功!");
    }
    else {
        Console.WriteLine("失敗!");
    }
}
Public Sub Main()
    Dim taiyaki As New たい焼き()
    taiyaki.中身 = "クリーム"
    If taiyaki.焼きます() Then
        Console.WriteLine("成功!")
    Else
        Console.WriteLine("失敗!")
    End If
End Sub



静的なクラス

静的クラスの場合はインスタンスを作成する必要がありませんので、つまりnewする必要がありません。(アクセス修飾子が正しければ)そのままクラス名を記述すれば勝手に使えてしまいます。

例えば以下のような静的クラスを作成したとします。

public static class 生地を作る静的クラス {
    public static void 生地作るよ() {
        //何かの処理
    }
}
public static void Main() {
    生地を作る静的クラス.生地作るよ();
}



しかし、これだとクラス名+メソッド名なので、クラス名が長いととてつもなく可読性の悪いコードとなってしまいます。
それを解決する方法として、usingディレクティブ(VB.NETの場合はImportsステートメント)を宣言します。これらを宣言することで、静的クラスを任意の略称で呼び出すことが可能となります。例えば、「生地を作る静的クラス」を「ki」という略称を宣言して使用してみます。

using ki = 生地を作る静的クラス;
public static void Main() {
    ki.生地作るよ();
}
Imports ki = 生地を作る静的クラス
Public Sub Main()
    ki.生地作るよ()
End Sub



当然ながら予約語は使用できませんので、分かりやすい・短い・適当な名前を付けるとよいでしょう。

Windows8以降の無線設定でWPA/TKIPを利用する方法

Windows7までは無線認証・暗号化方式でWPA/TKIPを利用できましたが、Windows8からセキュリティ上の問題からGUIの設定表示から削除されました。

でも、無線APがWPA-PSK/TKIPの設定でOS混在で使用する場合はどうしてもWPA/TKIPを設定しなければなりません。今回はその対処法です。

GUI上からは設定できませんので、一旦GUIから適当にWPA2/AESなどで設定しておき、コマンドから変更します。

ここでは、「SASAKI」というSSIDに対して変更する例です。
なお、「暗号化方式」→「認証の種類」の順番で変更しないとエラーとなります。

#暗号化方式をTKIPに変更
set profileparameter name=SASKI encryption=TKIP
#認証の種類をWPAに変更
set profileparameter name=SASKI authentication=WPAPSK