- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
377 :デフォルトの名無しさん[sage]:2016/08/28(日) 00:17:10.42 ID:+mkoP4gZ - >>375
それはオブジェクト指向がその意味でのシンタックスシュガーでしかないよ。 class構文が無かったCでも関数ポインタ+構造体で同じ事が出来たわけだし。 重要なのは拡張メソッドにthisが渡っていることだよ。 静的メソッド(クラスメソッド)にはthis使えないでしょ。 今回の例で言えば、宣言部分は以下になるということ。 ただしコンパイルが通るかどうかは知らんが。 通らないのなら君の言うとおり、通るのなら俺の言い分が正しいことになる。 public static void Draw(this Rectangle rect) public static void Draw(this Circle circle)
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
381 :デフォルトの名無しさん[sage]:2016/08/28(日) 00:31:26.64 ID:+mkoP4gZ - >>379
いや拡張メソッド内で自分自身のメンバを参照出来るよ。多分。 > 実際に、拡張メソッドは、それらが拡張している型のプライベート変数にはアクセスできません。 > https://msdn.microsoft.com/ja-jp/library/bb383977.aspx 普通に考えれば、わざわざプライベートは駄目って書いてある時点でpublicならいけるって事でしょ。 要するに外面的には Draw(circle) でしかないところを circle.Draw() と書けるだけでしかない。 その辻褄はコンパイラが合わせるけど、privateをpublicには出来ないのでこれは無理。 MSDNの説明には矛盾を感じないけど。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
382 :デフォルトの名無しさん[sage]:2016/08/28(日) 00:36:48.79 ID:+mkoP4gZ - >>379
> 拡張メソッドは親クラスからは呼べないからそれを定義してもShapeクラスから直接呼べない バインディングはコンパイル時だし、多分普通に呼べると思うよ。 というか呼べないと拡張にならないから、文法が違ったとしても何らかの呼び方は必ずあるはず。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
387 :デフォルトの名無しさん[sage]:2016/08/28(日) 01:12:12.64 ID:+mkoP4gZ - >>386
コンパイラ云々は俺は普通に出来ると思っているのだが、まあそれは置いておこう。 >>384 こちらを先にやろう。 > //できない > //shape.Draw(); これって出来ないんだっけ?というかこちらは最近はJavaScript(型無し)でやっていて、 void hoge(var shape ) // var なので何でも入る { shape.Draw(); // 勝手に直近のvirtualが呼ばれる。 } で普通にオーバーライドされたvirtualが呼べる(circle.drawなりrectangle.drawなり)から、 実際のC#での記述の仕方は知らない。 ただ、アップキャストしたらもう仮想関数は呼べません、ダウンキャストしてからにしてください、 だとOOPとしてはかなり無理があるから、自然なやり方がないわけ無いと思うけど。 だってその is 構文ってのも新しいんでしょ? だったらそれがなかったこれまではOOPできませんでしたーってことになっちゃうじゃん。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
389 :デフォルトの名無しさん[sage]:2016/08/28(日) 01:33:21.98 ID:+mkoP4gZ - >>384
以下見る限り、virtual付けたら普通にOOPできるとしか読めないが。 てか当たり前だが。 引き数渡ししたら出来なくなるって事もないと思うけど。 > しかし、動的な型に基づいて呼び出されるメソッドを決定したい場合があります。 > (というより、ほとんどの場合、メソッド呼び出しは動的に決定した方が都合がいい。) > 動的な型に基づいて呼び出されるメソッドを選びたい場合、 > 以下のように、 メソッドに virtual という修飾子を付けます。 > http://ufcpp.net/study/csharp/oo_polymorphism.html その上にvirtual付けなかった場合も書いてあるから見比べれば分かると思うよ。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
390 :デフォルトの名無しさん[sage]:2016/08/28(日) 01:37:09.54 ID:+mkoP4gZ - >>388
まあその通りだ。今は最新環境がないので試せない。 確かにこれ以上やっても意味無いので、後日最新環境で試せば良いだけだね。 「拡張メソッド」ってのに俺が夢を見すぎているのかもしれんし。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
393 :デフォルトの名無しさん[sage]:2016/08/28(日) 09:58:20.62 ID:+mkoP4gZ - >>391
試したぞ。VSCommunity2015はサインインしないと使えなかったから、VS2008EEだが。 その範囲では確かに出来なかった。コードは以下ページを参考に改変した。 > http://ichitcltk.hustle.ne.jp/gudon2/index.php?pageType=file&id=cs002_ExtensionMethods しかし理由は拡張メソッドがstaticだからだよ。以下コードで、 class SampleClass1 {} class SubClass : SampleClass1 {} static class hoo { static public void SampleExtensionMethod1(this SampleClass1 sampleClass1) { Console.WriteLine("super_ex"); } static public void SampleExtensionMethod2(this SampleClass1 sampleClass1) { Console.WriteLine("super_ex2"); } static public void SampleExtensionMethod1(this SubClass subClass) { Console.WriteLine("sub_ex"); } } static void test(SampleClass1 sampleClass1) { sampleClass1.SampleExtensionMethod1(); // (A) sampleClass1.SampleMethod1(); // (B) } static void test3(SubClass subClass) { subClass.SampleExtensionMethod2(); .// (C) }
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
394 :デフォルトの名無しさん[sage]:2016/08/28(日) 09:59:05.95 ID:+mkoP4gZ - (B)は通常メソッドで、SubClassが与えられると当然SubClassのメソッドが呼ばれる。
ただし(A)は常にSampleClass1(親クラスというより「そこに記述された」クラス)の拡張メソッドが呼ばれる。 なお(C)はSampleClass1の拡張メソッドが呼ばれる。 (つまり子→親に関しては辿れる) (B)が動作する以上、thisポインタは正しく渡っている。 あとは拡張メソッド側に継承関係を明記し、実行時型でこれを解決すれば良いだけなのだけど、 C#はどうやら文法的にこれを禁止している。(技術的には全く問題なく出来るはず) 今回出来ないのは、拡張メソッドがstaticだからコンパイル時にそこにリンクされるからだ。 そこでvirtual指定してみたが、これはSyntacErrorで落ちる。 どうやらそれ以前にC#は静的クラスは継承禁止で、当然virtualには出来ない。 しかしそもそも静的クラスが継承出来ないのが問題だ。 何故これを禁止しなければならないのかは分からない。 結局>>378の「static縛り」或いは上記「staticクラスは継承出来ない」のが問題。 とはいえこの仕様では「拡張メソッド」がイマイチなのはC#開発側も認識出来るだろうから、 何らかの理由があってこの仕様なのだとは思うが。 したがって>>364の指摘どおり、 > >>329で多態が必要なのに>>352は拡張メソッドでいいというのは矛盾してるよ。 これは間違いだった。拡張メソッドが多態出来ないのは知らなかったから。 となると>>352は ・Shapeクラスにメソッド追加---(α) ・GUI側でShapeクラスを継承したクラスを作って対応---(β) ・>>384方式で is XXXX 形式で全部書く---(γ) のどれかになるが、俺ならαかβであって、γはないね。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
396 :デフォルトの名無しさん[sage]:2016/08/28(日) 10:18:04.05 ID:+mkoP4gZ - 釣りか?
仮想関数とif/switch分岐でのコストはC++の連中が死ぬほど議論しているが、 結論はどっちもほぼ同じだよ。 だからαもβもγも動作速度は大して変わらないはずだけど。 しかしそもそもC#ってパフォーマンス重視出来る言語じゃないよね。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
398 :デフォルトの名無しさん[sage]:2016/08/28(日) 10:24:49.86 ID:+mkoP4gZ - じゃあどこだよ?
てかチャットじゃねーんだから意見があるのなら分かるように書けよ
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
409 :デフォルトの名無しさん[sage]:2016/08/28(日) 15:27:24.87 ID:+mkoP4gZ - >>405
先に了解事項を書いておく。 > >staticクラスは継承出来ない > なぜなら、それが定義だから(意訳) これは了解。こちらも調べている限り、どうやらそのようだと分かった。 staticクラスはシングルトンの糖衣かと思っていたが、わざわざ別物を用意しているね。 > 静的クラスはシールされるため、継承できません。 > https://msdn.microsoft.com/ja-jp/library/79b3xss3.aspx > staticクラスとSingletonでは以下の違いがあります。 > http://takachan.hatenablog.com/entry/2016/01/04/211414 したがって、疑問は何故staticクラスにしか拡張メソッドを置けないか、になり、 つまり>>378と同じで、そちらの見解は>>385というのもいい。 ただ俺はこの回答に納得しているわけではないが、 結局これはどこまでやるかということだから。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
410 :デフォルトの名無しさん[sage]:2016/08/28(日) 15:30:05.25 ID:+mkoP4gZ - 次に今回の実装方式について、
(α)についてはその通り。 (β)については「継承」が出来ればそっちの方がいいが、無理なら「内包」でも問題ない。(β+) コンストラクタでShapeを与えて内包したRectangleDraw、CircleDrawクラスを作り、 ShapeDrawクラスを継承する。 つまりラッパーオブジェクトを作成して同階層構成でXXXXDrawクラスを構成する。 これだと自前クラスなので何も問題なくOOPできるが、 コンストラクタ内では1回だけ is XXXX でswitchしなければならない。 それでも毎回記述が必要な(γ)よりマシ。 > クラス内にフィールドで保持してもいいが 後の文を読む限りこれはばらしてということだと思うが、 (β+)はばらさすにそのまま保持する。(フィールドとして Shape shapeを持つ) そして各Drawメソッドからは this.shape.xxxxとしてアクセスする。 こうすれば、アクセサに関してもShapeの定義そのままであり、追加記述は必要ない。 これにより、外枠以外の本質的記述が(γ)に対して増えることはない。 これでどうだ? 最後に以下だが、 > 実行時に解決するのは非常にコストが高い。 > 多分仮想関数を理解してないんだと思うけど、静的メソッドはC++やC#の方式だと仮想関数テーブルに無いので仮想関数になり得ない > また、呼び出しに失敗する可能性があるというリスクがあり、静的型付け言語の良さを殺している これはいろいろだいぶ違う気がするが、確認も時間がかかるし、後回しでいい。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
412 :デフォルトの名無しさん[sage]:2016/08/28(日) 15:50:02.41 ID:+mkoP4gZ - >>406
リンク先読んだ。筆者の考え方とは同意する。 結果、俺は以下に該当するのでオブジェクト側に突っ込みたいと考える。 > 型だけを見た単純な分岐しかしない場合、 > 仮想関数を使う方が実行性能面でも保守しやすさの面でもいいだろう。 そちらは以下に該当すると考えてパターンマッチングというのも了解だ。 > 1つは、型の外に実装を書く方が好ましい場合だ。(中略) > 特定の種類のアプリでしか使わないような機能を書く場所としては (型は)【追補】 > 不適切となる。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
413 :デフォルトの名無しさん[sage]:2016/08/28(日) 16:06:33.60 ID:+mkoP4gZ - >>405
ちなみに以下について俺の疑問点を先に挙げておく。 > 実行時に解決するのは非常にコストが高い。 > 多分仮想関数を理解してないんだと思うけど、静的メソッドはC++やC#の方式だと仮想関数テーブルに無いので仮想関数になり得ない > また、呼び出しに失敗する可能性があるというリスクがあり、静的型付け言語の良さを殺している ・静的メソッドはvtableに置く必要はないが、普通はそこに置いているはず。 それが一番簡単だし。 (クラス自体がstaticではないのでコンパイル時にリンクは無理) したがって、virtualメソッドとstaticメソッドの実行速度差は無いはず。 ・呼び出しに失敗って何?C#ならコンパイルが通っている限りあり得ないと思うが。 (C++みたいな<reinterpret_cast>とかは無しで) ・実行時に解決って言ったって、vtableの場合は解決済みのthisポインタを持っているわけであって、 何かを比較したりすることはない。間接呼び出しになるだけ。 コストはゼロではないが、リフレクションとかとは全く違う。 URL付きのツッコミは歓迎。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
414 :デフォルトの名無しさん[sage]:2016/08/28(日) 16:30:05.10 ID:+mkoP4gZ - 追記
俺の間違いが分かったので書いとく。 × > ・静的メソッドはvtableに置く必要はないが、普通はそこに置いているはず。(>>413) ○ 置いてない。静的メソッドは直接呼び出し。 × > したがって、virtualメソッドとstaticメソッドの実行速度差は無いはず。 ○ 直接呼び出しする非virtualの方がvirtualよりは速い。 staticメソッドには言及されていないが、多分非virtualと同じ。 > メンバ関数 > https://www.microsoft.com/japan/msdn/vs_previous/visualc/techmat/feature/jangrayhood/ 他の下2件はどこを調べればいいか分からないのでツッコミ待ち。 よろしく。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
416 :デフォルトの名無しさん[sage]:2016/08/28(日) 19:17:00.51 ID:+mkoP4gZ - >>415
いや>>352に対する君の主張は「毎回べた書き」だろ。すり替えるなよ。 それはさておき、通常はクエリメソッドを用意しておいてそれで対応だ。 例えば Shape.type で 'rectangle' や 'circle' が取れるとか。 だからパターンマッチング自体の記述は要らんよ。(見た目は同様ではあるが) というかそれが絶対に必要ならこれまでどうしようもなかったことになっちゃうだろ。 > ExtensionAttributeを静的クラスに付与して、コンパイルの時にそれを見てるだけ。(中略) > そうすると、存在しないメソッドは呼び出せないので失敗することになる だから何?コンパイラが通らないだけだろ。 話がずれているか? 上記話のすり替えをしてくる奴だから信用はしていないが。 >>405を見る限りこちらは「一般」の仮想関数だと認識して回答した。(>>413,414) そちらは ・「拡張メソッド」の動的解決はコストが高くて、仮想関数になりえなくて、呼び出しに失敗する ことを言っていたか? それなら「一般メソッド」について記述した>>413,414については見解の相違なしということでいいな。 > 他の言語がShapeクラスを継承した時に必ず拡張メソッドを書いてくれることは保証できない 当たり前。というか静的にリンクするんだから、namespaceに名前がない時点でコンパイルが通らないだけ。 使う気なら自前で定義なりusingしなければならないし、無ければ使えない。 多態する気なら、例えば多重継承として実装すればいいだけ。特別なコストはかからない。 実装方式は>>414のリンクに記述してあるとおり。 拡張メソッドを多重継承として実装出来ない理由があるか? > 利点が皆無 どこがだよ?やけくそになってないか? 拡張メソッドが多態出来ればメリットがあるのは見たら分かるだろ。 今多態出来ていないのは何か理由があるか手を抜いているか(今後対応するか)だよ。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
420 :デフォルトの名無しさん[sage]:2016/08/28(日) 20:23:47.93 ID:+mkoP4gZ - >>417
こちらの認識は、ID:gXVhUUHW = ID:hQF+q+Zk だ。 違っていたのならすまんかった。 > クエリメソッド クラスの中に複数種が入ることがある場合、(多態される場合) 一般的に「何が入っているか」は分かるようになっている。だからそれを使う。 C#の文法による解決は必要ない。(というかそうじゃないと動的型を取れない言語で使えない) > あと、「他言語は対応していない」の部分を完全に無視してるみたいだけど、そこも要点なんですが これについては関係ないとしか思えない。 拡張メソッドを自動的にエクスポート出来ると思っているのならそれが間違いだろ。 namespaceの中に拡張メソッドが見えていないと使えないのだから、 使う側が全部管理しないといけない。 同じ拡張メソッドを他でも使いたければ、その拡張メソッドを格納したクラスを使う側がusingしないといけない。 現在の実装はあくまで「コンパイラがstaticクラスのstaticメソッドに対しての関数呼び出し」に変更するだけ。 それがコンパイル時に出来なければコンパイルが通らない。他言語はどこに関係あるんだ? > 全く独立したクラスのstaticメソッドをどうやって多重継承するの? 多重継承は全く独立したクラスを継承することですが? 当然これをやる時はvirtualにする(今現在のC#の制限事項をはがす)のだが、ついて来れているか? 元々「継承出来ないクラス」(stat;icクラス)にしか拡張メソッドを置けないのが問題であって、 拡張メソッドをシングルトンに置ければ何も問題なくなる。 そしてそれが実現出来ない技術的障害はない、というのが俺の主張。 この辺を最初から知っていて傍観してるのが>>378だろ。 > ほぼ拡張メソッドじゃなくてさっき君が言ったShapeDrawクラスを使うべき案件 いやそっちの意見は何なんだよ? 君はパターンマッチングが一番良いと思ってるんだろ? 俺は、「拡張メソッドで多態」(α)(β)(β+)の順で検討する。 ただ第一候補が今は駄目なことを知らなかったから迷走したが。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
421 :デフォルトの名無しさん[sage]:2016/08/28(日) 20:32:09.16 ID:+mkoP4gZ - >>418
> そのどちらのライブラリも必要だったので同時に使おうとすると、rectangleの名前が衝突して区別できない!となる。 それは最初からポインタの型が違うから区別出来るだろ。 > ちなみに、、、、以降(ry それがないように、本来は「直接クラスを拡張」(α)するなり「継承」(β)するべきだろ。 やむなく内包(β+)なりパターンマッチング(γ)になった場合は変更の影響は受けるのは当たり前。 ただ、(β+)よりも(γ)の方が記述の変更量は多いよ。 その辺はMSDNにもそのまま書いてある。 > 拡張メソッドは、一般的に、必要な場合に限り注意して実装することをお勧めします。 > クライアント コードで既存の型を拡張する必要がある場合、 > 可能であれば既存の型から派生した新しい型を作成することで行ってください。 > 詳細については、「継承 (C# プログラミング ガイド)」を参照してください。 > 拡張メソッドを使用して、変更できないソース コードのある型を拡張する場合、 > 型の実装の変更により拡張メソッドが破損するというリスクを負うことになります。 > https://msdn.microsoft.com/ja-jp/library/bb383977.aspx
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
426 :デフォルトの名無しさん[sage]:2016/08/28(日) 21:37:38.11 ID:+mkoP4gZ - >>424
それが、「コンパイルには通るが拡張メソッドを呼ぶと失敗する」例だというのなら、 言いたいことは分かった。 ただ、それは失敗して当然というか、 「知らない図が書けない」だけであって、実装してない部分が動かないだけ。 丁寧にやるのなら、拡張メソッドがない場合に□を 出力して対応するしかないし、それだけでしかない。(文字化けのようなもの) (C#に実装の有無を確認する方法があるかどうかは知らない。 JavaScriptでは問題なく出来る。 拡張メソッドが多態出来るのなら、親(Shape)に□を表示させるメソッドを実装しておく) 別にそれは正しく全てを実装する時に手間が増えるわけでもない。 dllが他言語から供給されたとしても関係ない。 そもそもそちらの言うとおりShapeが演算用クラスであった場合、 クラスを追加する側にこちら『だけ』で使っている拡張メソッドを実装してくれと言うのが無理。 拡張メソッドは使う側が全部管理しなければならないだけ。実装も。 実装を強制する方法はないが、その必要もない。 自前で実装済みかどうかを判定して対応するのみ。 (定義を確認する方法がないのなら困ったことになるが、 それでも最悪try-catchは出来ると思うが、駄目なのか?)
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
427 :デフォルトの名無しさん[sage]:2016/08/28(日) 21:46:13.45 ID:+mkoP4gZ - >>425
> 構造体 これはShapeがクラスではなく構造体だった仮定か? それは最初から間違いで、クラスにしてもらうしかない。 どのやり方がいいのかは合意をとる必要はないけど、 俺が思うには「拡張メソッドで多態」が出来れば全て満足で上手く行く。 現状の拡張メソッドの仕様では旨味が無く、使いどころがない。 正直C#でこの手の「残念仕様」を見るのは初めてで、少し驚きだ。 (C#の仕様はどれもこれも何を意図しているか大体分かるものばかりだった) ただ、やる気だけの問題だと思うから、今後拡張される可能性はあると俺は思うけど。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
429 :デフォルトの名無しさん[sage]:2016/08/28(日) 21:55:53.66 ID:+mkoP4gZ - 追加
>>424 その場合、仮にdll側で拡張メソッドを実装してくれていたとしても、インポート出来ないだろ。 こちら側がコンパイル時に見えてないと駄目なんだからさ。 だから今の仕様は「全部自前で用意しろ」なんだよ。 俺もそれでいいと思うし。 拡張メソッドを実装したクラスをインポートするっていうことも出来る(ようになる)のかもしれんが。
|
- C#, C♯, C#相談室 Part91 [無断転載禁止]©2ch.net
434 :デフォルトの名無しさん[sage]:2016/08/28(日) 23:08:26.53 ID:+mkoP4gZ - >>433
何について言っている? 「拡張メソッドの多態」についてなら、既に言ったようにローカルに多重継承させればいいだけ。 実装自体も無理はないし、動作速度も問題ないと思うよ。 各クラスのvtableをコピーすることにはなるけど、 オブジェクトを直接vtableに載せていることはないだろうし、 コピー領域は、sizeof(void*)*フィールド個数 Bytes でしかない。 もちろん他の実装も出来るだろうし、いずれにしても必要になればMSが検討すればいいだけ。 多分ここら辺の話が通じないのは、君がvtableの実装を理解出来ていないのだと思うよ。 それは>>414にあるURLを全部読めばいい。 C++やC#のコンパイル言語は、メソッドは動的に名前で解決するわけではないんだよ。 (なおJavaScriptは全面的に動的解決だが、それでもC#の1.5倍ほど遅いだけ。) 俺は「拡張メソッドの多態」は欲しいと思う。 君はこれについて「要らない」のか「欲しいけど出来ないという意見」なのかはっきりしないが。
|