質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

オブジェクト

オブジェクト指向において、データとメソッドの集合をオブジェクト(Object)と呼びます。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

2回答

1554閲覧

【C#】メンバのvalueを動的&boxing回避で取得したい

yuya556223

総合スコア9

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

オブジェクト

オブジェクト指向において、データとメソッドの集合をオブジェクト(Object)と呼びます。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2020/08/04 16:29

編集2020/08/04 23:57

前提・実現したいこと

【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のポインタを取得する」方法が思いつかない。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2020/08/04 23:19 編集

リフレクションを許容できるならボクシングなど気にする必要ありません。気になるなら静的に取得してください。 強いて言うなら、目的によっては使えないかもしれませんが、データを取得するクラスを T4 などを使って自動生成すれば効率は良くなると思います。
退会済みユーザー

退会済みユーザー

2020/08/04 23:07

何故そういう事をしたいのか、具体的にどういう処理で使う事を想定しているのでしょう?
yuya556223

2020/08/04 23:39

@Zuishin アドバイスありがとうございます。 確かにリフレクションの負荷を考えると気にする必要は無いかもしれないですね。静的な取得では実現が難しいので気にしないことにします。 「データを取得するクラスをT4で自動生成」というのは与えられたTypeからGenericsを動的に生成するということでしょうか??
yuya556223

2020/08/04 23:44 編集

@Zuishin @radian 追記いたします。
Zuishin

2020/08/04 23:44

取得する対象のクラスがコンパイル時にわかっている場合に使える方法です。失念していましたが、unsafe が使えるならその方が楽で柔軟性も高いと思います。
Zuishin

2020/08/04 23:46

シリアライザなら T4 を使う方法は使えませんね。
yuya556223

2020/08/04 23:48

@Zuishin ですよね。ありがとうございます。 勿論unsafeを使うことも視野に入れていますが、FieldやPropertyからポインタを取得する方法が見つからないのです...ポインタを取得することができれば後はデータを抜き出すなり(C#ライクにするなら)IntPtrを取得-->MarshalからPointerToStructure(ptr)で取得ができると思うのですが...
Zuishin

2020/08/04 23:52

> FieldやPropertyからポインタを取得する方法が見つからないのです... & はメンバーに使えないんでしたっけ?
yuya556223

2020/08/04 23:59

@Radian MessagePackより汎用なシリアライザライブラリを作りたいのです。
yuya556223

2020/08/05 00:02

@Zuishin &は使えるのですが、動的に取得することは構文上難しいのです。 ですので、fieldInfoなどから変数のポインタがどこにあるのかを知る必要があると考えました。 〇できる int* ptr = &hoge; ×できない hogeは特定できないので int* ptr = &fieldinfoとclass object
Zuishin

2020/08/05 00:11

プロパティはどのみち関数なので呼び出すまでアドレスはありませんしね。やはりリフレクションを使うしか方法が無いように思えます。
yuya556223

2020/08/05 00:34

@Zuishin そうですよね。確かに言われればそうですね。 ありがとうございます。
guest

回答2

0

Unsafeクラスで直接メモリ操作すれば可能かもしれません
Unsafe クラス

投稿2020/08/04 23:26

編集2020/08/04 23:26
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuya556223

2020/08/04 23:43

アドバイスありがとうございます。 Unsafeでメモリ操作する場合、ポインタを取得する方法が必要なので、オブジェクトに含まれるFieldやPropertyのアドレスを動的に取得する方法があればよいのですが...
guest

0

自己解決

・C#のクラスのポインタは常に変化するので、その中のFieldやPropertyのアドレスを動的に取得することは難しい(Zuishin様より)
・このスキームを作る場合、Reflectionは許容せざるを得ないため、Boxingのコストは無視してもよいのではないか?(Zuishin様より)unsafeポインタ(radian様より)はあくまでクラスのアドレスをfixし、静的なポインタ取得をするのには向いているが、動的な取得時にはクラスオブジェクトをfixできないので難しい

【結論】
boxingはとりあえず許容するしかなさそう。

皆さま、丁寧な回答、ありがとうございました。
大変勉強になりました。

投稿2020/08/05 00:42

yuya556223

総合スコア9

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問