前提・実現したいこと
【C#】
- FieldやPropertyの値(の中でもstructやArrayを除くValue typeの変数)を動的に取得する
- (例えばReflectionにおけるGetValue(obj)などのように)boxingコストが発生するコードを避け、直接byte[]としてデータを取り出したい
- Expression TreeやILを用いた動的なコード生成によるboxing回避は極力避けたい(Compileのコストがかかる)
- "試した方法"にやったことを記載しておきます
- 何か「このような手法があるのでは?」ということがあれば教えていただけると幸いです
試した方法
1. GetValue
以下のようなコードを組み、順当にFieldInfoからGetValueするが、返り値は(たとえValue Typeであっても)objectであるため、boxing/unboxingコストが発生してしまう。(単純なフィールドからのGetValueだと1コードあたり0.2KBのGCゴミが発生していることが分かっている)
int data = (int) // ここでunboxingコストが生じています typeof(Hoge) // HogeクラスにはHugaField(int)が存在します .GetField("hugaField") // hugaFieldのデータはintと仮定します .GetValue(obj); // GetValueの返り値はobjectであるためboxingが発生します
2. FieldHandleを取得する
いまいち使い方が分からない。
var serInfo = new SerializationInfo(typeof(int), new FormatterConverter()); info.FieldHandle.GetObjectData(serInfo, new StreamingContext(StreamingContextStates.All)); console.log(serInfo.GetValue("FieldObj", typeof(object)))
このようなコードを組むと感覚的に「オブジェクトデータが出てくるんじゃね?」と思ってしまったが、出力結果はこのFieldInfoを基底クラスとしたRuntimeFieldInfo(internalだったと思います)が出てくるのであまり意味がなかった。正しい使い方を知っている方がいたらご教授願いたい。
3. 王道にgenerics
今回のコンセプトが、「クラス内にある変数を動的に抜き出す」ことなので難しいかなと。
(無理やりtypeof(Hoge).MakeGeneric(~~~)でメソッドを作りInvokeするのもありだが、コストがboxingした時より大きくなってしまう)
4. Expression Tree
うん十倍もGCゴミが発生してしまう。(compile後のFuncをキャッシュしようとしてもfieldNameは可変なのでキャッシュする意味がない)
var target = Expression.Parameter(typeof(object), "target"); var fieldName = "hugaField"; var body = Expression.Convert( Expression.PropertyOrField( Expression.Convert(target, typeof(Test)), fieldName), typeof(int)); var lambda = Expression.Lambda<Func<object, int>>(body, target); var func = lambda.Compile(); console.log(func(this));
5. Marshal
Marshal.PtrToStructure<int>()
を使えばIntPtrから変数を得ることができるが、IntPtrをどこから持ってくるのだろうか。。。
6. やけくそでC++からのextern
現実的な線かなとは思っているのですが、C++の深い知識が少ないもので、他の解決策があるのであればそちらを使いたい。
補足情報
- フレームワーク
.NET 4.xを使っていますが、最悪バージョンは気にしません。
- 開発環境
あまり関係ないとは思いますが一応Visual Studioです。
最後まで読んでいただきありがとうございます。
何かアイディアがある方、こういうの知っているぞという方、是非ご教授願いたく思います。
よろしくお願いいたします。
追記
なぜ実現したいのか?
独自汎用シリアライザを作るためです(ライブラリを作成している)。多くのシリアライザはネストしたクラスや多次元配列、複雑なgenerics、独自定義クラスなどを自由度高くシリアライズできるライブラリはない(と認識している)ので作ってみようと思った。しかしネストしたクラスの構造を動的に取得する以上、動的に変数を取得する必要があり、(boxingコストを気にしなければ実現は可能なのですが)パフォーマンス向上のために(boxingコストが大きいことがデバッグでわかったので)なるべく不要なboxingを無くしたいと考えた。
unsafeによるアクセス
unsafeを用いる場合、ポインタがわかっているのであれば取得できるのだが、「動的にFieldやPropertyのポインタを取得する」方法が思いつかない。
回答2件
あなたの回答
tips
プレビュー