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

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

新規登録して質問してみよう
ただいま回答率
85.48%
.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

Q&A

解決済

1回答

1350閲覧

ジェネリッククラスの算術演算処理

HiraKazu1124

総合スコア322

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

1グッド

1クリップ

投稿2022/04/24 08:33

よくある質問なのかもしれませんが、タイトルの通りジェネリッククラスでの算術演算子処理について質問があります。
環境の想定は.NET6.0C#10.0でもそれ以下でも構いません。(現在廃止されているようなものは避けたいですが👀)

実現したいこと

c#

1// 複素数クラス 2public struct Complex<T> { 3 public T Re { get; set; } // 実数部 4 public T Im { get; set; } // 複素数部 5 6 public Complex<T> Add(Complex<T> other) { 7 Complex<T> result; 8 result.Re = Re + other.Re; 9 result.Im = Im + other.Im; 10 return result; 11 } 12 13 //... 14}

上記はよくある複素数クラスの例ですが、ジェネリクスを用いて計算する場合の型を指定したいです。
ただ、このままだとタイプTに算術演算が実装されている保証が無いので、たとえComplex<double>と定義する予定だったとしても、演算処理でエラーが出るのは目に見えていますよね...😑

ただ、なんとかこんなことがしたいなぁと思っている所です。(将来的にdecimalとかでも計算させたい時が来そうで🥺)
実際には複素数とは全然別の目的ですが、かなり演算処理重視で書きたい方針です👀

昔からどうやるのか気になりながらもスルーしてきましたが、現実に作成しなければいけないときがやってきてしまいました😅

自分なりの案

私の案として思いついたのは...
(1) あきらめて、Tが取り得る全ての通りの実装をする(ComplexIntComplexDoubleみたいに👀)
(2) ジェネリクスは使わず、ReImobjectにする
(3) 算術演算可能インターフェースICalculatableを自作し、where T: ICalculatableを指定
(4) リフレクションを使う
(5) 式木を使う

とりあえず上記の方法は思いつきました。


考察としては...
(1)のメリット
・算術演算が遅くなる理由が無い。
(1)のデメリット
・かなり頭の悪い実装ww
・メンテナンスが大変
(int) + (double) -> (double)みたいな暗黙の型変換などを自分で実装しなければいけない

(2)のメリット
・is演算子で型を確認すればひとまず計算は出来る
・処理速度もほぼ落ちないと思う
(2)のデメリット
・実行時エラーがでる可能性がある(コンパイルエラーにならない)
・使う型をあらかじめ想定しておかないといけない(int, long, double対象とか)

(3)のメリット
Complex<T>構造体ではある程度便利に書けそう
・処理速度もそこまでは落ちないかな?
・自作クラスでさえも、ICalculatableを持たせれば対応させられる
(3)のデメリット
intdouble自体にICalculatableを持たせる事が出来ないので、他の構造体などでラップが必要
Complex<int> + Complex<double> -> Complex<double>みたいな実装方法が思いつかない😑

(4)のメリット
・簡単に動的に型を取得して演算可能
・自作クラスでさえも対応させられる
(4)のデメリット
・考えるだけで処理速度は大幅に落ちそう😨(場合によっては10倍じゃ済まないですよね...👀)
・実行時エラーがでる可能性がある

(5)のメリット
・動的に演算処理の式を作成可能(おそらくコンパイル時?)
・自作クラスでさえも対応させられる
(5)のデメリット
・処理速度はどうなんだろう🤔(コンパイル時に評価しているならまだまし?でも最適化は難しそう👀)
Complex<int> + Complex<double> -> Complex<double>みたいな実装方法が思いつかない😑

疑問点

処理速度が優先という条件下ではみなさんならどの方法で実装しますか?
他にも方法があるのならキーワードだけでも教えてほしいです。

(4)は無条件排除。
実装の幅の柔軟さなら(2)。(単体テストバリバリ書かないとなぁ...🥺)
実行時エラー排除優先なら(3)。

まぁ...ひとまず現状ではそこまで多くの型を扱うわけではないので、最悪(1)の手段も考えてはいます。
ただ、将来的なメンテが無茶苦茶怖いです...😨(ライブラリとして作っていますし👀)

(5)が一番謎...あまり経験が無いのもありますが、本当にコンパイル時に全て式が作成されるのでしょうか?
たしかにCompile()メソッドはありますが...
あと、

C#

1public struct Complex<T> where T: struct 2{ 3 public T Re { get; set; } 4 public T Im { get; set; } 5 6 static Complex() 7 { 8 // おそらくTの型を見て例外処理が必要なのかなぁ? 9 10 var param1 = Expression.Parameter(typeof(T)); 11 var param2 = Expression.Parameter(typeof(T)); 12 13 /* 14 AddValue = (T v1, T v2) => v1 + v2; 15 コメントの下の行は上記のように「コンパイル時」に展開されるという認識で正しいでしょうか? 16 例えば、Tに足し算の機能が持っていない場合コンパイルエラーになるのかな? 17 ただ、足し算の機能があるかどうか...は「operator+」があるかどうかで判断ということでしょうか? 18 (なんだか古い書き方っぽい気がしていますが...) 19 */ 20 AddValue = Expression.Lambda<Func<T, T, T>>(Expression.Add(param1, param2), param1, param2).Compile(); 21 } 22 23 // private staticで持つぐらいならComplexの外で定義するべきですね💦 24 // あくまで例と言うことで...😅 25 private static Func<T, T, T> AddValue { get; } = null!; 26 27 public Complex(T re = default, T im = default) 28 { 29 Re = re; Im = im; 30 } 31 32 public Complex<T> Add(Complex<T> other) 33 { 34 return new Complex<T>( 35 Re = AddValue(this.Re, other.Re), 36 Im = AddValue(this.Im, other.Im) 37 ); 38 } 39} 40 41//////////////////////////////////////////////////////////////// 42// ここからは少し贅沢な話かもしれませんが...(ここからは可能ならで大丈夫です) 43#if false 44Complex<int> c1 = new(1, 2); 45Complex<double> c2 = new(0.1, 0.2); 46 47// おそらく現状のComplexではコンパイルエラー 48Complex<double> result = c1.Add(c2); 49 50// 上記を実現するためには... 51public static implicit operator Complex<double>(Complex<int> other) => new(other.Re, other.Im); 52// みたいな定義が必要なのかもしれませんが、Complex<T>の中で定義できませんよね?👀 53#endif

そもそも(5)の方法(式木)を使うべきなのか...👀
それ自体よく分からないので、なにかスマートで処理速度が落ちにくい方法があればご教授頂きたいです。
C++のテンプレートなら余裕なんですけどね😅(まぁ...C#は型制約が厳しいのが良いところでもあるしなぁ🥺)

よろしくお願いします。

TN8001👍を押しています

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

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

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

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

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

Zuishin

2022/04/24 09:28 編集

> 処理速度が優先という条件下ではみなさんならどの方法で実装しますか? 思い付くものを全部書いて計測します。 > Complex<int> + Complex<double> -> Complex<double> これを実装しなければならない時点で(1)一択です。
HiraKazu1124

2022/04/24 11:26

> 思い付くものを全部書いて計測します。 あんまり質問自体否定するような助言をするなら無理に回答しなくて大丈夫ですよ💦あなたのことは別に技術が低いとは思っていませんが、その解答なら誰でも出来るし、それで解決することぐらいわかっています。「そんなこと質問するな」と書いた方がいいのでは? それに(4)も試すんですか?そんなこと言ってたら5通りじゃ効かないパターン思いつきますが? (1)一択の理由もまだしっくりこないんですよね🤔(1)で行く覚悟もないわけでは無いですが、最終手段ですね。 一つ型を増やすたびにそっくりなコードが増えていく未来が見えますよね🥺 いっそのことコードを書くプログラムを書くのも手でしょうかね😊
Zuishin

2022/04/24 12:04

誰でもできるなら自分で解決できたのでは?
HiraKazu1124

2022/04/24 12:06

あなたの追記は誰でも書けますよという意味です😊 意味が伝わりづらくてすみません。
Zuishin

2022/04/24 12:09

> これを実装しなければならない時点で(1)一択です。 これを誰でも(あなたでも)書けるなら、自分で解決できたのでは? と聞いています。
HiraKazu1124

2022/04/24 12:14

>> これを実装しなければならない時点で(1)一択です。 > >これを誰でも(あなたでも)書けるなら、自分で解決できたのでは? >と聞いています。 すいません。言っている意味が分かりません😅 「(1)が正しい」と言うこと前提の回答でしょうか? その定義が無ければ上記の回答は出てこないですね👀 私は「(1)が正しい」かどうかも含めて質問しているんですよ?
Zuishin

2022/04/24 12:15

正しいのではなく、ジェネリックではあなたのやりたいことはできないと言っています。 誰にでも書けることなので、あなたも少し考えたらわかると思いますが。
HiraKazu1124

2022/04/24 12:27

(a) >> 処理速度が優先という条件下ではみなさんならどの方法で実装しますか? > >思い付くものを全部書いて計測します。 (b) >> Complex<int> + Complex<double> -> Complex<double> > >これを実装しなければならない時点で(1)一択です ...と書いておいて (c) >ジェネリックではあなたのやりたいことはできないと言っています。 (a)では自分は答えを知っていると仮定した上で全部試すようにしたわけですね🤔 (b)は理由も書かずに(1)と言われても納得しないです。 (c)でやっと(b)の理由が「ジェネリックでは実現できない」という主張だと分かりましたが👀 別に「誰でも書ける」というのは(a)に対して私は書いていますが? 「誰にでも書けることなので、あなたも少し考えたらわかると思いますが。」 「(a)が理解できる人なら、(b)から(c)を推測できると思いますが。」 という主張にはあまりつながらないですね🤔 まぁ...人それぞれ答え方の色はあるでしょうしね〜 そもそも言葉の使い方がお互い違うんでしょう👀 まぁ...私の質問の中のほんの一部の事ですし、これ以上話しても無駄でしょう😑
Zuishin

2022/04/24 12:30

まだわかっていないようですが、やってみればすぐわかることです。
Zuishin

2022/04/24 12:33

言葉遣いに関しては、あなたの投稿はかなりうざいので、改めたほうが良いと思いますね。 他にそんな絵文字使ってる人いないでしょ?
HiraKazu1124

2022/04/24 12:36

>やってみればすぐわかることです。 その回答で済ますならそもそも質問サイトで回答しないでください >あなたの投稿はかなりうざいので、改めたほうが良いと思いますね。 だから言葉の使い方がお互い違うと言っているんです。 私は絵文字は使っても人に対して「うざい」と書いたことは一度もありません。 もう話がそれていてお互いマイナスなので止めましょう。 おそらく相性が悪いんです。
Zuishin

2022/04/24 12:43

回答してませんね。このくらいの質問が書ける人なら自分ですぐわかると思ったので。 実際はまだ何を言われてるかわからずとんちんかんなことを言ってるので、アレですが。 話をそらしたくないなら、余計な攻撃はしないようにしたらいいと思いますよ。
HiraKazu1124

2022/04/24 12:48

だから、これ以上話してもマイナスです。 何度言えば分かるんでしょうか? 多分それを書くと言うことはこちらから攻撃したという認識なんでしょうけど。 私はあなたの追記に攻撃されたと思ってしまったんですよ。 言葉の取違いがこれからも頻発するでしょうから止めてください。
Zuishin

2022/04/24 12:49

そういうことを言わないようにしたら? と言ったんですが、伝わりませんか?
HiraKazu1124

2022/04/24 12:51

伝わりませんね。 「うざい」と書く人に言われても。
Zuishin

2022/04/24 12:51

攻撃されたと思って攻撃したなら、今後は間違いだとわかった時点ですぐにやめたらいいですよ。 その時は謝罪があるとなお良いと思いますね。
Zuishin

2022/04/24 12:52

で、マイナスになることをいつやめるんです?
guest

回答1

0

ベストアンサー

Generic Mathですね。
Preview Features in .NET 6 - Generic Math - .NET Blog

環境の想定は.NET6.0、C#10.0でもそれ以下でも構いません。

それ以上(.NET7・あるいは.NET6 Preview)はダメですかね?^^;

注)ぶっちゃけ何もわかってません^^;

cs

1var a = new Complex<double>(1, 0); 2var b = new Complex<double>(0, 1); 3Console.WriteLine(a + b); // (1, 1) 4 5 6public struct Complex<T> where T : INumber<T> 7{ 8 public T Re { get; set; } 9 public T Im { get; set; } 10 11 public Complex(T real, T imaginary) => (Re, Im) = (real, imaginary); 12 13 public Complex<T> Add(Complex<T> other) 14 { 15 Complex<T> result = default; 16 result.Re = Re + other.Re; 17 result.Im = Im + other.Im; 18 return result; 19 } 20 21 public static Complex<T> operator +(Complex<T> t1, Complex<T> t2) => t1.Add(t2); 22 23 public override string ToString() => $"({Re}, {Im})"; 24}

現状下記環境で実行できました。
リリース時にどうなっているか(System.Numerics.Complex にも実装されるのかとか)はわかりません(どこを追えばいいのかがよくわからん^^;

Visual Studio Community 2022 Preview 17.2.0 Preview 4.0
.NET 6.0.300-preview.22204.3
NuGet Gallery | System.Runtime.Experimental 6.0.2


ちょっと違うんじゃないでしょうか。

あ、すいません。下のほう読んでませんでした^^;
となるとComplex<T>がIAdditionOperatorsになるみたいなことになるんでしょうか?(ほんとになんもわかってません^^;

これでいいんすね(難しく考えすぎてました。そのうえコメント時にも、ちゃんと質問を読めてませんでした^^;

cs

1Complex<int> c1 = new(1, 2); 2Complex<double> c2 = new(0.1, 0.2); 3 4Complex<double> result = c1 + c2; 5Console.WriteLine(result); // Complex { Re = 1.1, Im = 2.2 } 6 7 8record struct Complex<T>(T Re, T Im) where T : INumber<T> 9{ 10 public static Complex<T> operator +(Complex<T> left, Complex<T> right) 11 => new(left.Re + right.Re, left.Im + right.Im); 12 13 public static implicit operator Complex<double>(Complex<T> other) 14 => new(double.Create(other.Re), double.Create(other.Im)); 15}

投稿2022/04/24 11:00

編集2023/07/30 07:17
TN8001

総合スコア9321

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

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

HiraKazu1124

2022/04/24 11:57

.NET6.0で将来的には`Generic.Math`で便利に書ける可能性がありますね😊 知らない機能でした、ありがとうございます! 今回のような問題では、INumberはあるとかなり嬉しいですね😆 もう少し調べてこの機能が将来入ってきても良いような設計も視野に入れてみます! 回答ありがとうございました😊
Zuishin

2022/04/24 12:05

Complex<int> + Complex<double> を Complex<double> にしたいらしいので、ちょっと違うんじゃないでしょうか。
TN8001

2022/04/24 13:08

> ちょっと違うんじゃないでしょうか。 あ、すいません。下のほう読んでませんでした^^; となるとComplex<T>がIAdditionOperatorsになるみたいなことになるんでしょうか?(ほんとになんもわかってません^^; Complexは例に出されがちだと思うのでどっかに参考実装がありそうに思うんですが、見つけられませんでした(ちゃんとやるには重い(大変・量がある)ってこと?? [Updating the generic math draft proposal to include changes proposed for .NET 7 by tannergooding · Pull Request #257 · dotnet/designs](https://github.com/dotnet/designs/pull/257)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問