ChristieのC#への書き換えログ

ChristieのC#への書き換えログ

# メソッド等の置換一覧

java ConcurrentHashMap<> → C# ConcurrentDictionary<>
java map<> → C# Dictionary<>
java getClass() → C# GetType()
java getGenericSuperclass() → C# type.BaseType 補足あり
java ClassCastException → C# InvalidCastException
java ByteBuffer → C# MemoryStream

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
java
T data = null;
Class<T> clazz = null;
byte[] messsagePack = null;
MessagePack packer = new MessagePack();
// write read
messagePack = packer.write(data); 
packer.read(messagePack, clazz);


C#
T data;
Type clazz = null;
byte[] messagePack = null;
// write read
messagePack = MessagePackSerializer.Serialize(data);
MessagePackSerializer.Deserialize<T>(messagePack);

java TreeMap<> → C# SortedDictionary<>
java HashMap<> → C# Dictionary<>
java Class clazz → C# Type clazz
java LinkedBlockingQueue<> → C# ConcurrentQueue<> → BlockingCollection<>
java socket.getInetAddress().getHostName() → C# Dns.GetHostEntry(((IPEndPoint) socket.RemoteEndPoint).Address.ToString()).HostName
java clazz.getName() → C# type.Name java socket.getInputStream().read() → C# socket.Receive([格納する配列]) (intの長さが返る) java ClassNotFoundException → C# TypeLoadException java ArrayList<E> → C# List<E>


# MessagePackが使用されていたクラス

  • MessagePackDataGear
  • RemoteMessage

# 実装を飛ばしている物(仮置きしているもの)

IncomingTcpConnetion 送られてくるデータを格納する配列のサイズ
PriorityThreadPoolExecutor ThreadPoolの優先度実装


# Tips

C# object → 全ての継承元クラス Type → 型情報 T → ジェネリクス


javaのatomicintagerはc#に存在しない

intを読む, 代入するくらいは原始的が保証されているらしい 一度実装してみてダメなら volatile修飾子をつける→読み書きがスレッドセーフになるが、コンパイラで最適化されない https://ufcpp.net/study/csharp/misc_synchronize.html

intのインクリメントとかはこのメソッドを使ったほうがいいかも https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked?view=netcore-3.1


java public static <T> Class<T> [関数名] () { }
→ Class<T> は返り値の型
→その前の<T>はジェネリスクメソッドの型を表す

C# public static Type [関数名]<T> () { }
→Typeは返り値の型
→関数名の直後の<T>はジェネックメソッドの型を表す

java int.class vs Intager.class
javaは独自でそれぞれの型についてプリミティブ型とクラス型が用意されているらしい… https://stackoverflow.com/questions/22470985/integer-class-vs-int-class/22471247

java側のdatagear/WrapperClassいらないかも… もうちょい調べる


javaでは
T typeJava = null;
はできるがC#では初期化でT型にnullを代入できない

T型の初期化は
T typeCs = default(T);
とする。(T)は省略可能


java getGenericSuperclass() → type.BaseTypeについて どちらも直接のスーパクラス、直接の継承元のTypeを返す

javaの場合 Object クラス、インタフェース、プリミティブ型、または void を表す場合、null が返され、配列の場合はClassが帰ってくる

C#の場合 直接の継承しているTypeを返す、もし現在のTypeがobjectかインターフェースならnullが返る


javaの場合
ジェネリスククラスの宣言時にジェネリスクの型宣言は省略できる
ex: Datagear dg;

C#の場合
ジェネリッククラス宣言時にジェネリックの型宣言は省略できない
ex: Datagear<Type> dg;


MessagePackはjavaではread,writeにはジェネリックを使用できるが、 C#ではジェネリスクを使用することができない
→ラッパーか何かを挟んで型判断してread,writeするしかない?
C#で関数名が全然異なっていた…

javaリファレンス
http://msgpack.org/javadoc/current/org/msgpack/MessagePack.html#read(byte[],%20java.lang.Class)

C#README https://github.com/neuecc/MessagePack-CSharp#quick-start


C#のenumの扱いについて

C#のenumではstringは許容されていない また、enum内でフィールド変数や関数を含めることはできないので、ヘルパークラスを実装してそちらを利用する必要がある

enum内でfor文などは使えないので、今回は別途関数を作成した コンストラクタで使用するとか…?


java HashMapとC# Dictionaryの違いについて https://stackoverflow.com/questions/1273139/c-sharp-java-hashmap-equivalent null周りなど違いがあるので注意


C#のBlockingCollectionは既定ではConcurrentQueue、つまりFIFOになっているが、特に使う理由はないのでConcurrentQueueを最初から利用することに →いやブロッキングどうするねん →やっぱり使う https://www.atmarkit.co.jp/ait/articles/1802/07/news019.html →使用後にDisposeしないといけない →無理…? →もしかしたらずっとBlockingCollection利用してるかも →Getするところでlengthが0になったもしくは 破棄をしていたらdisposeする

# BlockingCollection使用箇所(Dispoes処理)

  • Connection
  • DataGears → fin
  • WaitList → fin

C#だとsynchronized修飾子がないので

1
2
3
4
5
6
7
private object syncObject = new object();

public void Function(){
    lock (syncObject) {
                
    }
}

でロックをかける必要がある


javaだとジェネリッククラスを型として変数宣言する際にはジェネリックの型宣言は必要ないが、 c#では変数宣言する際に型宣言が必須なので、今回はとりあえずobject型で宣言している。

DataGearの宣言は基本的にDataGear<object>で


java側で出力されるsocket.getInetAddress().getHostName() localhost:3333

これと同様の結果をC#でも欲しい感じ

javaはSocketクラスが割となんでもしてくれる感じがあるが、 C#はSocket, Dns, IPAdress, IPEndPointとかなり役割が分散されているので注意


daemon/Connection

Close()について socket.shutdownでSocketShutdown.Bothで本当にあっているか


java socket.getChannel().write(buffer);について

javaのChannelではwrite(), read()などをByteBufferなどと一緒に使ってデータの送受信ができる。 Channelの開き方は、socketとは別にopenする必要があるので注意。


christleに導入されているMessagePackのバージョンは0.6以前のもの そのため、シリアライズするクラスには@Messageが必要になる https://github.com/msgpack/msgpack-java/wiki/QuickStart-for-msgpack-java-0.6.x-(obsolete)

java版は0.7.xから仕様がかなり変わっているため注意が必要

C#版のMassagePackには日本人が作成したものを利用している(Unityとかでもサポートされているらしい) https://github.com/neuecc/MessagePack-CSharp#quick-start


javaとC#のenumの仕様はかなり異なっており C#ではenumにメソッドを生やすことができない

最初はヘルパークラスを実装することで対応しようとしたが、挙動的にenum各要素を初期化する必要があるとわかったため、独自にenumの挙動をするクラスを作成した

https://qiita.com/lilacs/items/167a73fbbfedf83eb51a https://bleis-tift.hatenablog.com/entry/20080808/1218133517

必要なのはenumをintにする変換なので、C#ではenumをintにcastできるので廃止


protected internalについて


LinkedBlockingQueueとConcurrentQueueの特徴について

  • LinkedBlockingQueue

    • FIFO
    • boolean contains(Object o): 要素の確認
    • peek(): 先頭取得、削除しない
    • poll(): 先頭取得、削除
    • put(): 末尾に追加 完了するまでブロック
    • add(): 末尾に追加 失敗時例外
      • put, addの違いについては:https://docs.oracle.com/javase/jp/8/docs/api/java/util/concurrent/BlockingQueue.html#add-E-
  • ConcurrentQueue

    • FIFO
    • スレッドセーフ
    • Enqueue(): 末尾に追加

Socketはaccept()するとその返り値にacceptされたsocketが返る
よってそれを変数に入れて利用する


javaのThreadはThreadを継承して作成をする。
Threadの名前をつける必要あり

c#は Task task = Task.Run(() => MethodThread()); task.Run(); (名前をつける必要なし)


IncommingTcpConnectionなどのスレッドはAcceptThreadで一括でTask.Runする


IncomingTcpConnetion

送られてくるデータ長がわからないので とりあえず4096と置いている


TCPで送受信するデータは、Remotemessage(Command), length, dataの順で入っている

PutCommand Command IncomingTcpConnection を参照


javaではserverSocketクラスがあり、listenerはこれを使えばいいが、c# 側にはない。

ここで注意するのが、javaではacceptでlistenを開始するが、C#ではListenでlistenを開始することに注意


優先度付きThreadPoolについて
javaのほうでは実装されているが、現状処理が複雑なため一旦実装を飛ばす
優先度が実装されている理由についてはheartbeatを実装したかったと考えられる

Taskは内部でThreadPoolを作成しているらしい https://oita.oika.me/2016/02/18/task-and-threadpool/

優先度をつけるならThreadPoolはTaskを利用できないかも


Task.RunとTask.Factory.Startnewについて

無限ループさせるような、すぐには終了しないTaskについては LongRunオプションをつける必要がある。

これは、Task.Runは裏でThreadPoolを作るが、この数がTask.Runの場合規定数(通常だと16(cpuに依存))に制限されており、無限ループのTaskも個の規定数にカウントされてしまうため
https://oita.oika.me/2016/02/18/task-and-threadpool/

それを避けるためにStartnewでTaskを作成する

https://qiita.com/chocolamint/items/3e2e4951ea0fa2ccd19e


C#のTaskでのThreadPoolについて

https://stackoverflow.com/questions/35734051/c-sharp-task-thread-pool-running-100-tasks-across-only-10-threads


lockのwaitとnotifyについて
Javaでは排他制御としてsynchronized(lock)(lockはobject型)を使用することで排他制御ができる。
解除にはlock.notifyなどを使う https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html#notify()

C#ではMoniterクラスの中にwaitやpulse関数があり、同じように使えるっぽい https://docs.microsoft.com/en-us/dotnet/api/system.threading.monitor.pulse?view=net-5.0

1
2
3
4
Object lock = new Object;
synchronized(lock) {
	lock.notify();    // lockの解除
}
1
2
3
4
objecy lockObj = new object;
lock(lockObj) {
	Moniter.Pulse(lock);
}

csprojectのStartupObjectでnamespaceを含めた書き方は

<StartupObject>Christie_net.AttributeCheck</StartupObject>
のようにドットでつなぐ 


フィールド変数などにつけたattributeは次のように取得できる

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class AttributeCheck {
    [Take] private int num = 0;
    [Peek] public string name = "riono";
    [PeekFrom("name")] protected string nextName = "local";
    
    
    private static void Main(string[] args) {
        AttributeCheck attributeCheck = new AttributeCheck();

        foreach (var field in attributeCheck.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic 
                                                                           | BindingFlags.DeclaredOnly | BindingFlags.Instance)) {
            foreach (var attribute in field.GetCustomAttributes(false)) {
                Console.WriteLine(attribute);   
            }
        }
    }
	
//  Result
//  Christie_net.annotation.Take
//  Christie_net.annotation.Peek
//  Christie_net.annotation.PeekFrom

BindingFlagsについては下記を参照
https://dobon.net/vb/dotnet/programing/typegetmembers.html


# TODO

  • annotation → fin
  • daemon
    • connection → fin
    • ChristieDaemon → fin
    • AcceptThread → fin
    • IncomingTcpConnection → fin
    • OutboundTcpConnection → fin
    • ThreadPoolExecutors 実装
    • Config → toporogyManager作成時に必要?
      • log4netかNlogのどちらかを使う
  • codegear
    • CodeGear → fin
    • InputDataGear → fin
    • CodeGearManager → fin
    • CodeGearExecutor → fin
  • datagear
    • command
      • Command → fin
      • CommandType → fin
      • CommandBuilder → fin
      • TakeCommand → fin
      • その他のcommand → fin
    • dg → fin
    • DataGearManager → fin
    • DataGears → fin
    • WaitList → fin
    • LocalDataGearManager → fin
    • RemoteDataGearManager → fin
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy