none
C#における静的ローカル変数の代用品 RRS feed

  • 質問

  • C++では、関数内でstatic変数が定義できましたが、http://msdn.microsoft.com/ja-jp/magazine/dd992855.aspxを読んだところ、C#ではCLRが静的ローカル変数をサポートしてないためC#ではサポートしていないと記述されていました。

    そこで質問なのですが、C#では静的ローカル変数の代わりとなる新しい機能が実装されてたりするのでしょうか?

     

    宜しくお願いします。

     

    2011年11月6日 10:32

回答

  • 外池と申します。以前、別の機会でまったく同じ議論がありました。

    その際には、関数内の静的ローカル変数を許す場合に、

    staticでないクラスの、staticでないメンバー関数の中で、静的ローカル変数を宣言するとして、staticなフィールド変数に相当するのか? staticでないフィールド変数に相当するのか? 曖昧である。

    なので、曖昧になる可能性を排除しようということで、関数内の静的ローカル変数は許していないのだろう・・・、ってことでした。VB.Netの場合・・・、どっちなんでしょうね?


    (ホームページを再開しました)
    • 回答としてマーク 毎日 2011年11月7日 10:22
    2011年11月7日 0:40
  • JavaScriptで、「状態を持った関数」としてクロージャを使うことはあるんですが、
    あまり良くない手段なんでしょうか。
    C#では使ったことないですが・・・

    static void Main(string[] args)
    {
        var func1 = CreateFunc(10);
        func1(); // 10
        func1(); // 11
    }
    
    static Func<int> CreateFunc(int i)
    {
        return () => i++;
    }
    


    • 編集済み femp 2011年11月7日 6:05 補足
    • 回答としてマーク 毎日 2011年11月8日 1:07
    2011年11月7日 6:03
  • VB2005のローカルなstatic変数と
    VC++2005のローカルなstatic変数は
    スコープが違うんですよね。
    VB2005はインスタンスごとで
    VC++2005はクラスごとなようです。
    C#でもローカルなstatic変数を宣言できると便利だと思いますね。
    以下 VC++2005での実験
    #include "stdafx.h"
    #include <iostream>
    
    class StaticTest{
    public:
        void StaticMethodTest();
    };
    
    void StaticTest::StaticMethodTest()
    {
        static int I=0;
        I++;
        std::cout << I << std::endl;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::cout << "start" << std::endl;
        StaticTest ins1;
        ins1.StaticMethodTest();
        ins1.StaticMethodTest();
        StaticTest ins2;
        ins2.StaticMethodTest();
        ins2.StaticMethodTest();
    	return 0;
    }
    
    結果
    start
    1
    2
    3
    4
    
    

    以下 VB2005での実験
    Module Module1
        Sub Main()
            Dim wk1 As New Class1
            Dim wk2 As New Class1
    
            wk1.StaticTest()
            wk1.StaticTest()
            Console.WriteLine("■■■")
            wk2.StaticTest()
            wk2.StaticTest()
        End Sub
    End Module
    
    Public Class Class1
        Public Sub StaticTest()
            Static I As Integer = 0
            I += 1
            Console.WriteLine(I.ToString)
        End Sub
    End Class
    
    結果
    1
    2
    ■■■
    1
    2
    
    
    


    • 回答としてマーク 毎日 2011年11月8日 23:56
    2011年11月8日 20:42

すべての返信

  • C、C++ の static と同等の機能は C# にはないので private なフィールドを用意してそれを使うことになると思います。
    2011年11月6日 11:04
  • 解答有り難うございます。

    やはり、class内の変数として定義するしかないということでしょうか・・・

     

    それにしても、何でCLRは静的ローカル変数をサポートしなくなったのでしょうか?

    関数の中での事象は関数の中で完結させる記述をした方が、他者が見た時の可読性が向上するという判断なのでしょうか?

     

    2011年11月6日 13:13
  • VBならサポートしてるんですけどね。
    絶対にないといけない機能とも思いませんが、あると便利ですね。
    2011年11月6日 13:52
  • そうですね。VB.Netはサポートしているみたいですね。

    CLRが静的ローカル変数をサポートしてない都合上.Net frameworkとしては使えないですけど、VB.Netはコンパイル時にクラス内に特殊名の変数として追加することでstatic変数をサポートしているようですよね。

    こういう風に実装するなら、C#も含めればいいのにとか思ったりもしますが、元々、静的ローカル変数は可読性の都合上あまり利用して欲しくはなかったけど、VBはC#と違って今まで使われてた言語なので、今まで使われていたソースをそのまま使おうと思うとstaticを実装しておかざるをえなかったってとこなんでしょうね。

     

     

    2011年11月6日 14:22
  • 外池と申します。以前、別の機会でまったく同じ議論がありました。

    その際には、関数内の静的ローカル変数を許す場合に、

    staticでないクラスの、staticでないメンバー関数の中で、静的ローカル変数を宣言するとして、staticなフィールド変数に相当するのか? staticでないフィールド変数に相当するのか? 曖昧である。

    なので、曖昧になる可能性を排除しようということで、関数内の静的ローカル変数は許していないのだろう・・・、ってことでした。VB.Netの場合・・・、どっちなんでしょうね?


    (ホームページを再開しました)
    • 回答としてマーク 毎日 2011年11月7日 10:22
    2011年11月7日 0:40
  • 関数内にstatic変数を持つということは、関数自身が状態を持つという意味で、それはオブジェクト指向としては正しくない実装です。(関数ではなく、オブジェクトが状態を持つべき)
    # yieldやawaitは気のせいw 

    そういった思想から、C#では関数内にstaticがサポートされていないのだと思います。また、関数でなく、メソッドと呼ぶのも少しはそういう意図が含まれているのかなとか。C++はメンバー関数ですし、VBはSubやFunctionなのにC#はあえてMethodなので。

    2011年11月7日 0:59
  • ああ、確かに。
    静的でないメンバー関数の中で静的ローカル変数を定義した場合、普通に考えると作成したclassのインスタンス毎にclass内変数として実装されていると考えられますが、言われてみれば静的ローカル変数がどのレベルまで静的なのかには言及してないですね。
    class test {
    	test() { };
    	int foo(bool f,int i=0){
    		static int a=0;
    		if(f){
    			a=i;
    		}
    		return a;
    	}
    }
    test c1=new test();
    test c2=new test();
    c1.foo(true,10);
    c2.foo(false);//a=0? or a=10?
    
    
    こんな感じで記述すると確かに曖昧と言われれば曖昧だなーと思います。
    VB.Netの場合はコンパイル時にclass内に特殊な名前の変数として追加することで実装しているようです。
     
    2011年11月7日 1:30
  • JavaScriptで、「状態を持った関数」としてクロージャを使うことはあるんですが、
    あまり良くない手段なんでしょうか。
    C#では使ったことないですが・・・

    static void Main(string[] args)
    {
        var func1 = CreateFunc(10);
        func1(); // 10
        func1(); // 11
    }
    
    static Func<int> CreateFunc(int i)
    {
        return () => i++;
    }
    


    • 編集済み femp 2011年11月7日 6:05 補足
    • 回答としてマーク 毎日 2011年11月8日 1:07
    2011年11月7日 6:03
  • うーん。。。classhoge.funcという関数をクラス名まで含めて一つの関数と考えると、このclassがフィールド変数持ってれば、funcは状態を持つ関数と考えられるんでは?とか思ってしまいますが・・・

    というか、私はオブジェクト指向の定義があまりよく分からないんですよね^^;;;

    カプセル化することをオブジェクト指向というなら、関数化してる時点でカプセル化してるじゃん、と・・・

     

    2011年11月7日 6:07
  • 試してみましたが実際C++/CLIがその方法で実現しています。VB.NETも同じですが。

    言語仕様として許すかどうか、と、言語仕様をどのような方法で実現するかは別問題ということです。C#は思想として認めないということかと。

    なお、C++の場合、言語仕様としてマルチスレッドが考慮されていないため、VB.NETとは異なりロックがなされていません。staticキーワードの説明でも

    Assigning a value to a static local variable in a multithreaded application is not thread safe and we do not recommend it as a programming practice.

    なんて記述もあります。 

    2011年11月7日 6:30
  • 静的でないメンバー関数の中で静的ローカル変数を定義した場合、普通に考えると作成したclassのインスタンス毎にclass内変数として実装されていると考えられますが、言われてみれば静的ローカル変数がどのレベルまで静的なのかには言及してないですね。


    う~ん、インスタンス毎にクラス変数というのはないですよね? クラス変数というのはインスタンスに関係なくクラスで一つ存在するものですから。おそらく発想としてメソッドのみ特別扱いされて、 メソッドのインスタンスが複数存在するように考えられているのだと思いますが、そのようなことはできません。一つのインスタンスがメソッドの複数のインスタンスを持つのであればどのレベルまで静的かというのは意味があるのですが、そうではない以上、静的と言えば、どのような場合でもクラスに一つだけです。
    であれば、メソッド内に静的変数があろうと、メソッド外に静的変数があろうと私は構わないと思います。メソッド内に静的変数があっても私はオブジェクト指向の概念から根本的に外れているとは思いません。その関数を使う側から見れば、メソッド内に静的変数があろうとメソッド外に静的変数があろうと関係のないことです。ですからVBがメソッド内静的変数を認めていることに積極的に肯定はしませんが、反対もしません。

    では、どうしてCLRでメソッド内変数を認めていないのでしょうか? これは私の想像になりますが、簡単に言えばその必要が無いからだと思います。静的フィールドを用意すれば良いだけですし、静的フィールドは時としてバグの温床になりますから、できるだけ限定した使い方のみを許しているのではないかと思います。VBはそれよりも暗黙のインスタンスなどの例があるように使い勝手を優先しているのだと思います。

    #メソッドを一つのインスタンスというより、メソッドを一つのオブジェクトと捉えているという方が意味が伝わるかもしれません。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2011年11月7日 6:32
    モデレータ
  • なお、C++の場合、言語仕様としてマルチスレッドが考慮されていないため、VB.NETとは異なりロックがなされていません。


    C++の場合、static云々は関係なく、スレッドセーフではないと思いますが。

     

     

    • 回答の候補に設定 NF64 2011年11月16日 1:43
    2011年11月7日 12:48
  • 質問文にリンクされているVB.NETについての説明、

    静的ローカル変数の初期化では多少の作業が必要になります。 GetMessage メソッドの MSIL を見ると、62 の命令があることがわかります (これに対して、クラスレベルのフィールドのみを使用する同様のメソッドの命令は 13 です)。 これらの命令のほとんどは、i の初期化をチェックするためと、ロックを使用してスレッド セーフな初期化を行うためのものです。

    との対比で引用しました。C#に対してどういうstatic変数を期待しているのかな?という意味も込めて。

    2011年11月8日 0:37
  • >fempさん

    そういえば、Eventとかにラムダ式で代入したりしてたから、C#はクロージャをサポートしていましたね^^;

    ただ、静的ローカル変数の実装方法としては少々めんどくさい感じがしますよねー。

    2011年11月8日 1:06
  • >trapemiyaさん

    クラス変数って静的メンバ変数っていう意味があったんですね-^^;

    (誤)インスタンス毎にclass内変数として実装されていると→(正)インスタンス毎にメンバ変数として実装されていると

    というのが正しい表記になります。更に、もう少し明確に記述すると、以下のようになります。

     

    静的でないメンバー関数の中で静的ローカル変数を定義した場合、普通に考えると作成したclassのインスタンス毎にメンバ変数として実装されていると考えられますが、言われてみれば関数内での静的ローカル変数がどのレベルまで静的なのかには言及してないですね。

     

    という意図を書きたかったのです・・・^^;

    バグの温床になるというのは、マルチスレッドだと起こりやすそうですよね。

     


    • 編集済み 毎日 2011年11月8日 2:50 メンバ変数とすべきものが違ってた
    2011年11月8日 1:46
  • 関数内での静的ローカル変数がどのレベルまで静的なのかには言及してない

    C++の関数内staticに相当するものと質問に挙げられていたので、クラスstaticが相当し、それで解決していたのかと思っていました。

    クラスstatic変数(静的フィールド)、インスタンス変数、どちらにしても関数が状態を持つ・持ち方に関する議論になってしまうため、私の最初のコメント「関数自身が状態を持つという意味で、それはオブジェクト指向としては正しくない」という主張は変わりませんが。

    2011年11月8日 2:17
  • (誤)インスタンス毎にclass内変数として実装されていると→(正)インスタンス毎にメンバ変数として実装されていると

    というのが正しい表記になります。

    了解しました。

    静的でないメンバー関数の中で静的ローカル変数を定義した場合、普通に考えると作成したclassのインスタンス毎にclass内変数として実装されていると考えられますが、 

     ここがおそらく私とずれているところです。静的というのはインスタンスには属さずクラスに属するということです。よって、インスタンス毎に静的というのはありえないと思います。よくある例え話ですが、たい焼き器と実際に焼かれたたい焼きがあります。たい焼き器がクラスで、たい焼きがインスタンスです。静的なものはたい焼き器に属していますので、毎日が戦場さんの言われることを私がイメージすると、たい焼きの中にたい焼き器が含まれているようなものになります。状態がインスタンス毎に違うのであればたい焼きの中に持てば良いですし、そうでなければたい焼き器に持ってしまうのが効率が良いでしょう。
    メソッドはこの状態に関わる場合がありますし、関わらない場合もあります。メソッド、いやサブルーチンと言い直します。サブルーチンは状態を持ちません。これに状態を持たせたいという発想からサブルーチン+データの形、つまりメソッド+フィールドのオブジェクトの基本形が出来ました。メソッドが状態と関わるのであればたい焼きの中に持てば良いですし、そうでなければたい焼き器の中に持てば良いのです。

     実際のところ毎日が戦場さんは、クラスレベルのグローバル変数、インスタンスレベルのグローバル変数をイメージされているのだと思いますが、静的というのはグローバル変数を表すものではなく、インスタンスには属さず、クラスに属することを意味するものです。クラスに属するということが結果的にグローバル変数と同等になるますので、そのような発想につながっているのであろうと想像します。
    インスタンスメソッド内にグローバル変数が定義できれば便利かもしれませんが、複数のインスタンス間で共通に使いたいのであれば、前にも述べましたが静的フィールドで充分ですし、あえて複数のインスタンス間のみでグローバルになる変数を定義できる必要はあまり感じません。
    #.NETに存在するプロパティはメソッドとフィールドの中間的なものですが、これは必要があってオブジェクト指向の基本形に付加されたものです。このような形で複数インスタンス間のグローバル変数というのは今後も付加されないと思います。理由は上で述べた通りです。VBで認められているのは、ふと思ったのですが、モジュールがあるのも理由の一つかもしれませんね。

    バグの温床になるというのは、マルチスレッドだと起こりやすそうですよね。
     

     静的な変数に関してはマルチスレッドで扱う場合には考慮しないといけませんが、マルチスレッドで扱わなくても静的な変数は見通しが悪くなり、特に改修などの際に影響度をきちんと調査しないと危ないので、必要最低限で使用すべきでしょう。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月8日 3:25
    モデレータ
  • >trapemiyaさん

     

    まだそんなに時間たってないからと思って、投稿を修正しておいたのですがすでに返信を記入され始めてたらしく大変申し訳ありません。

    訂正したものが更に間違ってました(滝汗)

     

    (誤)class内変数として→(正)メンバ変数として

     

    が正しいです。。。

    2011年11月8日 9:46
  • 本題ではなく VB.NET の話になりますが、
    佐祐理さんが書かれたスレッドセーフの件を補足させてもらいます。

    VB.NET の静的ローカル変数はスレッドセーフだけど、C++/CLI は違う、
    というわけではないですね。

    太字で引用された通り、初期化はスレッドセーフに行われますけど、変数としては全くスレッドセーフではないので、VB.NET の方にも C++/CLI のような注意書きがあっても良いぐらいかなと思いました。
    (質問文でのリンク先のサンプルコードの "i = i + 1" は、int なローカル変数なのにマルチスレッドでは同期が必要になります。)

    実際はクラスのメンバー変数で実現されている、という予備知識(説明書き)がなければ、int のようなローカル変数(ただし静的)をマルチスレッドのために考慮しないといけないなんて普通は思わないですから。

    この点において、このわかりづらさは C# にはいらないと思いました。(^^;

    2011年11月8日 12:23
  • VB2005のローカルなstatic変数と
    VC++2005のローカルなstatic変数は
    スコープが違うんですよね。
    VB2005はインスタンスごとで
    VC++2005はクラスごとなようです。
    C#でもローカルなstatic変数を宣言できると便利だと思いますね。
    以下 VC++2005での実験
    #include "stdafx.h"
    #include <iostream>
    
    class StaticTest{
    public:
        void StaticMethodTest();
    };
    
    void StaticTest::StaticMethodTest()
    {
        static int I=0;
        I++;
        std::cout << I << std::endl;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::cout << "start" << std::endl;
        StaticTest ins1;
        ins1.StaticMethodTest();
        ins1.StaticMethodTest();
        StaticTest ins2;
        ins2.StaticMethodTest();
        ins2.StaticMethodTest();
    	return 0;
    }
    
    結果
    start
    1
    2
    3
    4
    
    

    以下 VB2005での実験
    Module Module1
        Sub Main()
            Dim wk1 As New Class1
            Dim wk2 As New Class1
    
            wk1.StaticTest()
            wk1.StaticTest()
            Console.WriteLine("■■■")
            wk2.StaticTest()
            wk2.StaticTest()
        End Sub
    End Module
    
    Public Class Class1
        Public Sub StaticTest()
            Static I As Integer = 0
            I += 1
            Console.WriteLine(I.ToString)
        End Sub
    End Class
    
    結果
    1
    2
    ■■■
    1
    2
    
    
    


    • 回答としてマーク 毎日 2011年11月8日 23:56
    2011年11月8日 20:42
  • >Aketi Jyuuzouさん

    うわー・・・

    これは怖いですね。

    言語によってこういう違うの知らなかったら絶対バグ作っちゃいます^^;;;

    こういう言語による違いがあることを知った上で使いこなせば便利かもしれませんが、明確に違いが分かるようにメンバ変数か静的メンバ変数で実装するようにしたC#は正しい気がします・・・

     

    2011年11月8日 23:55
  • まだそんなに時間たってないからと思って、投稿を修正しておいたのですがすでに返信を記入され始めてたらしく大変申し訳ありません。

    あ~、そうでしたか。class内変数という単語はあまり聞かないので、どういう意味なのかなと一瞬思いましたが、流れでクラス変数のことだろうと思っていました。こちらこそ確認不足&思い込みがありました。確認不足ですみません。

    ところで、C#ではなぜメソッド内の静的変数を認めていないのだろうと思って探してみました。以下のC# teamのブログが見つかりましたが、回答をもう少し突っ込んで書いてほしかったと思います。

    Why doesn't C# support static method variables?
    http://blogs.msdn.com/b/csharpfaq/archive/2004/05/11/why-doesn-t-c-support-static-method-variables.aspx

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月9日 0:59
    モデレータ
  • >trapemiyaさん

    こちらのURLは具体的な例は示してないですが、結局、Aketi Jyuuzouさんが提示している記述の曖昧性が一番の原因という感じがしますね。

    それにしても、偉い人は具体例ぐらい分かってんだろ?って感じで書かれることが多いんで読んでて困ることが多いです(´・ω・`)

     

    2011年11月9日 14:38
  • 曖昧性があれば言語として成り立ちません。C++、VBはそれぞれそういう仕様で設計されているというだけのことです。各言語間で共通の決め事はありませんから、同じ記述をしても違いが出るのは仕方のないことです。例えば、アクセス修飾子においてjavaとC#でprotectedは違う意味になりますし、アクセス修飾子を指定しなかった場合も違う振る舞いをします。javaとC#ではprotectedの意味が曖昧だからprotectedを無くしちゃえということにはならないでしょう。
    各言語において仕様をきちんと確認し、思い込みでコーディングしないようにしなければなりません。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月10日 0:49
    モデレータ
  • それにしても、偉い人は具体例ぐらい分かってんだろ?って感じで書かれることが多いんで読んでて困ることが多いです(´・ω・`)

    そういわれてもフォーラムに書き込まれていることが全てであり、毎日が戦場さんがどこまでのものを求めているのかわからないので、「C#における静的ローカル変数の代用品」と言われたらC++のそれに相当するものを挙げるわけです。

    ですので例えば外池さんが

    staticでないクラスの、staticでないメンバー関数の中で、静的ローカル変数を宣言するとして、staticなフィールド変数に相当するのか? staticでないフィールド変数に相当するのか? 曖昧である。

    と言われていますがC++仕様に照らし合わせたら曖昧ではなく「staticフィールドが相当する」に決まっています。毎日が戦場さんに合わせるよりも仕様に合わせて話をする感じでしょうか。

    わからない部分は追加質問されればいいと思いますよ。

    2011年11月10日 1:27
  • >trapemiyaさん

    >曖昧性があれば言語として成り立ちません。

    すみません、曖昧性という表現は適切では無かったかもしれませんね。

    静的ローカル変数の意図の曖昧性によるバグフィックスの困難性というべきなんでしょうか。

     

    javaとC#でprotectedの挙動が違うことに関しては、これに変わる代用品が無いですが、静的ローカル変数は使用しなくてもメンバ変数や静的メンバ変数で明確に実装できるのでこちらを推奨するということかなと。

     

    >佐祐理さん

    何か勘違いさせてしまったみたいですみません。偉い人が、、、というのは別に佐祐理さんのことを言ったわけでなくhttp://blogs.msdn.com/b/csharpfaq/archive/2004/05/11/why-doesn-t-c-support-static-method-variables.aspxのQ&Aに関して言ったことです。

    >C++仕様に照らし合わせたら

    外池さんが問題にしていたのはC++の仕様が曖昧か否かではなく、コード記述者にとって曖昧だという意図だと私は解釈してました。

     

    2011年11月10日 22:56
  • 静的ローカル変数の意図の曖昧性によるバグフィックスの困難性というべきなんでしょうか。

    う~ん、曖昧性っていうのは一つの言語内ではありえませんので、曖昧性とおっしゃっているのは言語によってスコープが違うので曖昧だということですよね? つまり、C#でメソッド内静的変数を認めてしまうと、それはC++と同じスコープになるのか、VBと同じスコープになるのかわからないから曖昧になると。でも、C#としてこういうスコープだと決めてしまえば良いだけのことじゃないでしょうか? 私にはC#からメソッド内静的変数を除いた理由が、他の言語でそのスコープがまちまちだから他の言語を知っている人が誤って使わないように省いたとは思えないのですが・・・。要するに、他の言語の仕様を気にすることが第一の理由で、C#がメソッド内静的変数を除いたとは私には思えないのです。除いたと思われる理由は下に書きます。

    javaとC#でprotectedの挙動が違うことに関しては、これに変わる代用品が無いですが、静的ローカル変数は使用しなくてもメンバ変数や静的メンバ変数で明確に実装できるのでこちらを推奨するということかなと。

    同じprotectedというキーワードを使うという前提があれば確かに代用品はありませんが、C#がprotectedというキーワードを使わなければ代用品はいくらでもあることになります。もし、C#が他の言語との曖昧性を気にするのであれば、protecedというキーワードを使わかったでしょう。
    現実問題として、他の言語と意味が違うという曖昧性を排除することはできません。将来、どんな言語がどういう意味で同じキーワードを定義するかわからないからです。よって、他の言語と意味が違う曖昧性を排除することを第一の理由として言語を設計しても、それはいつ崩れるかわからないことです。

    ところで、私の感覚ではメソッド内静的変数のふるまいは曖昧ではありません。VBが特殊なだけという感覚です。私は多くの言語の仕様を知らないのですが、オブジェクト指向の観点からそのように思います。
    ですから、C#がメソッド内静的変数を採用したとしても、私は全く曖昧性を感じません。staticはクラスに属する変数ですから、インスタンス毎に存在するとは思いもしないでしょう。もし、インスタンス共通のスコープをC#が持つとすれば、それは新しいキーワードとして設けるべきでしょう。繰り返しになりますが、インスタンス共通のスコープを持つ変数の必要性が高くないため、C#はそのような機能を設けていないのだと思います。
    したがって、インスタンス共通のメソッド内静的変数は存在しないことになりますし、クラス変数としてふるまうメソッド内静的変数であれば静的フィールドとして定義できます。よって、C#においてメソッド内静的変数を設ける積極的な理由が無いことになります。

    上記の感覚で言えば、VBのみが特殊だと思います。私には無理やりメソッド内静的変数を実現しているように見え、したがって、オブジェクト指向の感覚からは外れているように映ります。前にも述べましたが、これが即悪いとは思いませんので、否定はしませんが積極的に賛成もしません。
    その特殊なVBの例と比較して、言語によって曖昧だという表現はちょっと違うと思います。VBは既定のインスタンスなどがありますので、誤ってオジェクト指向の観点から踏み外さないように気を付ける必要があると思います。

    ちょっとしつこいようで申し訳ありませんが、このままではすれ違ったまま終わってしまいそうでしたので書かせていただきました。私も至らないところがあり、上記が必ずしも全て正しくないかもしれませんが、何かの考えるきっかけになればと思っています。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月11日 2:28
    モデレータ
  • 外池です。言葉足らずで申し訳ありません。

    もちろん、「曖昧」とは、言語仕様の曖昧さのことではありません。プログラマー側の言語仕様の誤解を招きやすいという意味での「曖昧」です。さらに言えば、C#が世に出る前の言語仕様の段階での議論として、伝聞したことをお伝えしたまでです。

    仕様が固まって言語が世に出れば、プログラマーは、判りやすくなくても、頑張って仕様を理解するしかありませんよね・・・。

    ちなみに・・・、私はtrapemiyaさんとは、まったく反対の印象を持っています。(あくまで、「印象」の話なので良し悪しの議論は止めておきましょう)

    VBは、「Shared」と「Static」と二種類のキーワードを使い分けていて、VBはVBなりに「ハッキリ」していると思います。あまり特殊という感じはしません。

    C++は「static」というキーワードを5つのコンテクストに分けて、それぞれどういう意味かを説明しています。
    http://msdn.microsoft.com/ja-jp/library/s1sb61xd.aspx
    これは、理解はできますが、好きにはなれません。

    C#は「static」の意味を一本に絞っていて、判りやすいと思っています。
    http://msdn.microsoft.com/ja-jp/library/98f28cdx(v=VS.100).aspx
    この意味を保ったまま、メソッド内のstaticなローカル変数という仕様を導入するのは、私もアリと思いますが・・・、無くても気にしないかな。


    (ホームページを再開しました)

    • 編集済み 外池 2011年11月11日 8:10
    2011年11月11日 3:37
  • > 上記の感覚で言えば、VBのみが特殊だと思います。私には無理やりメソッド内静的変数を
    > 実現しているように見え、したがって、オブジェクト指向の感覚からは外れているように映ります。

    単語の選択や将来性については、trapemiya さんが書かれているとおりだと思いますが、若干 VB を補足すると、VB では、C# や C++ の static に相当する機能に割り付けられた単語は shared であって、C# や C++ に、VB の static に相当する機能や予約語がないというだけですね。

    shared = 変数や関数、手続きの定義される場所をクラスに移動する。(C# や C++ の static に相当)

    static = 変数や関数、手続きの定義される場所をインスタンスに移動する。

    というかんじですね。単語が同じなので shared と static を混同されているだけかな?と思いました。

    2011年11月11日 3:47
  • >trapemiyaさん

    >オブジェクト指向の観点からそのように思います。
    大変申し訳ないのですが、よろしければこの場合どの様にオブジェクト指向の観点からずれているのか説明して頂けないでしょうか?

     

    曖昧性に関して少し思ったのですが、MSが.Net framework向けに提供している言語がC#を除いてC++,とVBっていうのが大きいんじゃないかなと。

    VBと比較するのはイレギュラーと仰ってましたが、これまでMS的にはVBを押してきてたので、そのMSの次世代プラットフォームの開発言語としてのC#を開発する場合、考慮される曖昧性とはC++とVBから来る曖昧性が大きいんじゃないかなと思いました。

    なので、少なくともC++でもVBでも.Netの開発が出来ることを考えると新規開発するC#からは省きたい不整合だったんじゃないかなと。。。

     

    2011年11月11日 4:20
  • 外池さんとK.Takaokaさんに言われて気が付きました。VBは別にsharedというキーワードがありますから、staticというキーワードがメソッドで共通の値を保持するための宣言であるということをうっかりしていました。お二人のご指摘通りです。というわけで上で私が発言しているVBに関することは全て訂正させていただきます。上で発言していることはstaticをsharedという意味で使っています。ごめんなさい。

    大変申し訳ないのですが、よろしければこの場合どの様にオブジェクト指向の観点からずれているのか説明して頂けないでしょうか?

     これも上と同じで私がsharedとうっかり脳内変換していたためでした。sharedとは別にstaticというキーワードを使っていますので、オブジェクト指向の観点からずれているわけではないですね。
    ごめんなさい、忘れて下さい。 ただ、オブジェクト指向的に必ずしも必要な機能ではなく、VB6からの移行のために仕方なくサポートしたように思えます。

    VBと比較するのはイレギュラーと仰ってましたが、これまでMS的にはVBを押してきてたので、そのMSの次世代プラットフォームの開発言語としてのC#を開発する場合、考慮される曖昧性とはC++とVBから来る曖昧性が大きいんじゃないかなと思いました。

    なので、少なくともC++でもVBでも.Netの開発が出来ることを考えると新規開発するC#からは省きたい不整合だったんじゃないかなと。。。

     

    ここについてはやはり私はそうは思わないのです。なぜなら省くしかないわけではなく、本当にこの機能をサポートしたければ、VBのstaticのように新しいキーワードを用意する方法もあるからです。
    逆にC#やC++しか知らない人間がVBでstaticを見ると、まず間違いなくクラス変数だと想像します。.NETであるC#とVBは同時に開発されましたから、複数言語間でトータルでキーワードを考察していたのであれば、C#とVBで意味が異なるstaticというキーワードを見直していたのではないでしょうか? ではなく実際にはstaticというキーワードが複数言語間で違う意味で存在するわけですから、あまり言語間での曖昧性というのは考慮されていないと思うのです。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月11日 6:00
    モデレータ
  • >trapemiyaさん

    >ただ、オブジェクト指向的に必ずしも必要な機能ではなく、VB6からの移行のために仕方なくサポートしたように思えます。

    VB.Netの静的ローカル変数については私もそう思います。http://msdn.microsoft.com/ja-jp/magazine/dd992855.aspxの説明にもあるとおり、.Net Frameworkとしては静的ローカル変数をサポートしていないのでVB.Netはコンパイル時に静的ローカル変数を特殊な名前のメンバ変数として追加することで実装しているようですから。

     

    >ここについてはやはり私はそうは思わないのです。なぜなら省くしかないわけではなく、本当にこの機能をサポートしたければ、VBのstaticのように新しいキーワードを用意する方法もあるからです。
    >逆にC#やC++しか知らない人間がVBでstaticを見ると、まず間違いなくクラス変数だと想像します。

    確かに、VBのstaticのように新しいワードを追加すればその言語単体を見た場合はロジカルで整合性がとれているとも取れると思います。しかし、世間的にはC++はメジャーであり、trapemiyaさんも仰しゃっているとおり、C++とかしか知らない人間がVBのstaticをを見るとクラス変数と認識される可能性が高いことから、明確な代替機能がある静的ローカル変数はC#では省く方向になったのではないかと思いました。

     

    >.NETであるC#とVBは同時に開発されましたから、複数言語間でトータルでキーワードを考察していたのであれば、C#とVBで意味が異なるstaticというキーワードを見直していたのではないでしょうか?

    これはC#とVB.Netが同時に開発されたということですよね?VBに関しては私の感覚では言語を開発したと言うより、.Net Framework作ったからそれを記述するのに何使うかって考えたときに、Microsoftの歴史のようなVBを外すわけにはいかないからVBで.Net Framekworkで動かせるようにコンパイラ作ったというニュアンスが正しいんじゃないかなと。

    そして、ここでC#とVBでstaticの意味が異なることを言うのはちょっと論点がずれてる気がします。VBには静的ローカル変数がありますが、C#には無いので、静的ローカル変数としてのstaticは比較できないからです。

    ここで、比較すべきはVBとC++のstaticではないでしょうか?

    そして、私の感覚では、C#はMSがVBを含めてプログラム開発において抱えていた問題を解決するために作成した.Net Frameworkで動くプログラムを記述するために作った言語なのでC++とVBの意味が異なるstaticキーワードを見直した言語を作成したということじゃ無いでしょうか?

    そもそも、VB6とVB.Netは違う言語なんだよと言ったところで、ユーザーは確実にVB6の時のソースも流用したいでしょう。その際にVBの名前を冠してる言語がいきなりstaticの定義変えましたとは言えないと思います。こういう大きな変更をする場合には、やはりC#のように新しい言語を作ることで実現するというのが誤解を避けるためにも適切だと思います。

     

    2011年11月11日 16:04
  • 確かに、VBのstaticのように新しいワードを追加すればその言語単体を見た場合はロジカルで整合性がとれているとも取れると思います。しかし、世間的にはC++はメジャーであり、trapemiyaさんも仰しゃっているとおり、C++とかしか知らない人間がVBのstaticをを見るとクラス変数と認識される可能性が高いことから、明確な代替機能がある静的ローカル変数はC#では省く方向になったのではないかと思いました。

    すみません。この文章の意味がわかりませんでした。C#からメソッド内静的ローカル変数が省かれる理由が、「C++とかしか知らない人間がVBのstaticをを見るとクラス変数と認識される可能性が高い」と書かれていますが、なぜこれが理由になるのかもう少し詳しく説明していただけませんでしょうか? 

    私の言いたかったことがうまく伝わっていない気がしましたので、もう少し具体的に書かせていただきます。

    C#でインスタンス間で共通となるメソッド内静的ローカル変数をサポートするのであれば、例えばcommonというキーワードを新設して以下のように記述できるようにすることもできます。

    private void Hogeメソッド()
    {
         common int a;    //インスタンス間で共通となるメソッド内静的ローカル変数
    }

    C#でクラス変数となるメソッド内静的変数をサポートするのであれば、C++のようにstaticで十分だと思いますが、VBのそれと意味が違ってきます。(VBでメソッド内静的ローカル変数をstaticで定義するとインスタンス間で共通となる)
    C++とVBでstaticの意味が既に違っている以上、C#においてメソッド内でstaticを使うとどちらの意味になるか人によって勘違いされる可能性が否定できません。よって、新しいキーワードを作るのも一つの手でしょう。この新しいキーワードはクラス変数となる静的変数ですから、例えばcstaticという名前にしましょう(ネーミングにセンスない気が・・・)。

    private void Hogeメソッド()
    {
         cstatic int a;    //クラス変数となるメソッド内静的変数
    }

    しかし、実際にはC#は上のどちらも採用していません。なぜでしょうか?
    最初のcommon(インスタンス間で共通となるメソッド内静的ローカル変数)については、クラス変数となる静的フィールドで十分代替できます。毎日が戦場さんが上で紹介されているページでも推薦されていません。VB6からVBへの移行のためにサポートされているようです。
    2番目のcstatic(クラス変数となるメソッド内静的変数)についてはどうでしょうか? C++ではサポートされています。しかし、結果的には静的フィールドになるので、これも必ずしも必要ではありません。個人的にはこれもメソッド外に静的フィールドを定義すれば良いだけですから、無い方がシンプルで良いと思います。

    よって、C#でメソッド内静的ローカル変数、およびメソッド内静的変数をサポートしていないのだと考えています。どちらも単なる静的フィールドで代替可能だからです。C++やVBなど他の言語との兼ね合いとか全く関係なく、ましてやstaticというキーワードがどうのこうのという前に、C#単体として見た時にどちらも必要ないと思うからです。これが私が一番言いたいことであり、最初の私からの質問に戻るのですが、なぜ毎日が戦場さんが思われているように、C#でメソッド内静的ローカル変数がサポートされなかった理由が、C++とVBでメソッド内staticの意味が違うからとなるのかが理解できないのです。おそらく、噛み合わない理由はここ一点だと思いますので、これについてもう少し詳しく教えていただくと、私の疑問も晴れていくのだと思います。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月12日 5:39
    モデレータ
  • >trapemiyaさん

    仰しゃりたかったことが段々分かってきました。

    C++とVBで静的ローカル変数としてのstaticの意味は異なっているが、C#を作る上で静的ローカル変数を廃止せずとも、C++の静的ローカル変数とVBの静的ローカル変数に相当する、cstaticとistaticのような新しいワードとして実装すれば混乱を避けることが出来る。しかし、C#は静的ローカル変数自体を実装することはしなかった。

    C++とVBでメソッド内staticの意味が違うからC#で静的ローカル変数がサポートされなかったとするなら、何故C++/CLIとVB.Netが互いの言語間で意味の異なる静的ローカル変数をstaticで定義し続けているのか?

     

    ということを仰しゃりたかったという解釈で良いのでしょうか?

     

    C++/CLIとVB.Netが意味の異なる静的ローカル変数をstaticで定義し続けている理由は単純に過去のソースを使えるようにするためだけだと私も思っています。だから、新しい言語としての名前を与えず、C++やVBという名前を冠していると思います。

    そして、過去の言語が抱えていた問題を修正した新しい言語としてC#を作る上で、VBとC++で意味の異なる静的ローカル変数も排除されたのではないかと考えています。

     

    また、私もcstaticとistaticのような新しいワードを追加して静的ローカル変数を実装するのは悪くないと思います。

     

    2011年11月12日 15:55
  • C++とVBで静的ローカル変数としてのstaticの意味は異なっているが、C#を作る上で静的ローカル変数を廃止せずとも、C++の静的ローカル変数とVBの静的ローカル変数に相当する、cstaticとistaticのような新しいワードとして実装すれば混乱を避けることが出来る。しかし、C#は静的ローカル変数自体を実装することはしなかった。

     その通りです。

    C++とVBでメソッド内staticの意味が違うからC#で静的ローカル変数がサポートされなかったとするなら、何故C++/CLIとVB.Netが互いの言語間で意味の異なる静的ローカル変数をstaticで定義し続けているのか?

    これは毎日が戦場さんも書かれている通り、過去のソースからの移行を無視できないという理由だけだと思います。 

     

    そして、過去の言語が抱えていた問題を修正した新しい言語としてC#を作る上で、VBとC++で意味の異なる静的ローカル変数も排除されたのではないかと考えています。

    新しいC#という言語を作成する際に、C++やVBで定義できるメソッド内静的変数(クラス変数)、および静的ローカル変数(インスタンス間で共通)は必要ないと判断したんでしょうね。C#は新たに開発された言語ですから、過去のソースからの移行を一切考えずに、純粋に必要か必要じゃないかで機能を設計できるわけですから、C#の言語として必要ないと判断すれば何の問題もなくその機能を落とすことができると思いますので。もし必要だと判断したのであれば、 

    また、私もcstaticとistaticのような新しいワードを追加して静的ローカル変数を実装するのは悪くないと思います。

     という実装が可能だったと思います。しかしその前にCLRでサポートされていませんから、CLRを設計する段階で不必要と判断されたんでしょうね。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月13日 4:36
    モデレータ
  • そして、過去の言語が抱えていた問題を修正した新しい言語としてC#を作る上で、VBとC++で意味の異なる静的ローカル変数も排除されたのではないかと考えています。

    最初に書きましたが、VBとC++の違いに影響されたのではなく、「オブジェクト指向」という考え方に照らし合わせたとき、関数が状態を持つということそのものが悪なのでC#では静的ローカル変数が排除されたのだと、私は考えています。

    ちなみにC++/CLIで静的ローカル変数があるのは、C++言語にそれがあるからです。そしてC++言語にあるのは、C言語にもあるからです。そしてC言語にはクラスの概念がありませんから、C言語と同じふるまいを考えたとき、C++言語・C++/CLIにおいてもクラスインスタンスではなくclass staticなメンバーになります(ならざるを得ない)。

    VBの方は知りません^^;

    2011年11月14日 0:39
  • > 佐祐理さん

    オブジェクト指向プログラミングの定義は以下の機能を有効活用するプログラミングのことですよね?

    カプセル化 (振る舞いの隠蔽とデータ隠蔽)

    インヘリタンス (継承) -- クラスベースの言語

    ポリモフィズム (多態性多相性) -- 型付きの言語

    ダイナミックバインディング (動的束縛)-- インタープリタの言語

     

    オブジェクト指向的に「関数が状態を持つということそのものが悪」という理由が今ひとつ分からないのですが・・・

    もう少し具体的に説明して頂けないでしょうか?


    • 編集済み 毎日 2011年11月14日 18:56 <li>タグを用いるとHPのバグで表示が崩れたため
    2011年11月14日 0:49
  • ページが崩れてしまって読めませんね…。
    2011年11月14日 5:22
  • ここのページは<li>タグを用いるとバグるようですね・・・

     

    2011年11月14日 18:57
  • そんな高レベルな話をしていません。もっと根本の根本、オブジェクト指向の原点についてです。

    手続き型プログラミング#オブジェクト指向プログラミングとの比較でも挙げればいいでしょうか。

    手続き型プログラミングでは、プログラミングはデータ構造とルーチンの集合に分割される。一方オブジェクト指向プログラミングでは、プログラミングはオブジェクトに分割される。

    辺り。手続き型プログラミングでは、データ構造とルーチンの集合に分割されます。その上でルーチン(C言語では関数)にstatic(関数呼び出しを超えて値を保持し続ける)データを持たせる機能が、質問にある静的ローカル変数です。ルーチンに必要なデータを持たせてしまうことで、逆にルーチンとデータ構造が分割されます。しかし、オブジェクト指向プログラミングでは、そういったデータはオブジェクトに持たせます。

    挙げられた4つの中であえて選ぶとすればカプセル化に沿っていない、ということでしょうか。関数呼び出しを超えて値を保持し続けることで、その関数を呼び出すたびに得られる結果が変わります。呼び出し側は関数の振る舞いがどのように変化するかを意識しながら呼び出す必要があり、それはカプセル化できていないことになります。

    例えば乱数について、C言語ではrand()関数内に情報を保持し呼び出すたびに新しい乱数を返しますが、C#ではRandomクラスインスタンスを作成し情報はインスタンス内に保持し、Next()メソッドで新しい乱数を返します。

    静的ローカル変数がないからこのような設計になっているわけではなく、オブジェクト指向に沿って設計をすると静的ローカル変数が必要なくなるのです。

    2011年11月15日 0:55
  • まさか、<ul>もしくは<ol>タグを付けずに<li>を書きましたか?
    # 修正されていて確認できませんでしたが、可能性の1つとして。
    2011年11月15日 0:56
  • http://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)

    Wikipediaのオブジェクトの項を見る限り「オブジェクト: object)はオブジェクト指向プログラミングにおいて、プログラム上の手続きの対象を抽象化する概念である。」とあるので、「関数⊆オブジェクト」ということですよね?

    関数自体もオブジェクトの一つと見なせば、関数が状態を持つことは別段オブジェクト指向の概念から外れているとは思えないのですが・・・

     

    というか、「手続き型プログラミング≠オブジェクト指向プログラミング」ではなく、手続き型プログラミングを発展させたのが、オブジェクト指向プログラミングのようですから、「手続き型プログラミング⊆オブジェクト指向プログラミング」ということではないのでしょうか?

     

    2011年11月15日 5:24
  • Wikipediaのオブジェクトの項を見る限り「オブジェクト: object)はオブジェクト指向プログラミングにおいて、プログラム上の手続きの対象を抽象化する概念である。」とあるので、「関数⊆オブジェクト」ということですよね?

    関数自体もオブジェクトの一つと見なせば、関数が状態を持つことは別段オブジェクト指向の概念から外れているとは思えないのですが・・・

     

    というか、「手続き型プログラミング≠オブジェクト指向プログラミング」ではなく、手続き型プログラミングを発展させたのが、オブジェクト指向プログラミングのようですから、「手続き型プログラミング⊆オブジェクト指向プログラミング」ということではないのでしょうか? 

    横からすみません。
    オブジェクト指向プログラミングがいう「オブジェクト」とは 「責務を持つ実体」 という定義であり、関数はそれを実現する手段の一つに過ぎないと考えてます。
    私のOOPに対する捉え方は以下の書籍をベースにしてますが、機会があれば一度お読みになられることをお勧めします。名著です。

    デザインパターンとともに学ぶオブジェクト指向のこころ
    http://www.amazon.co.jp/dp/4894716844

    あと「カプセル化」については、以下のブログの記事が何かの参考になるかもしれません。
    http://d.hatena.ne.jp/bleis-tift/20090201/1233426011

     


    ひらぽん http://d.hatena.ne.jp/hilapon/
    2011年11月15日 5:38
    モデレータ
  • 「手続き型プログラミング≠オブジェクト指向プログラミング」ではなく、手続き型プログラミングを発展させたのが、オブジェクト指向プログラミングのようですから、「手続き型プログラミング⊆オブジェクト指向プログラミング」ということではないのでしょうか?

    全く違います。中国語を発展させたのが日本語だから中国語⊆日本語 とは言わないでしょう。

    端的に言えば

    手続き型プログラミング
    データ構造とルーチンを分けて扱う
    オブジェクト指向プログラミング
    データ構造とルーチンをひとまとめにし、オブジェクトとしてそれを扱う

    です。全く対立する概念です。データを伴わない関数単体をオブジェクトと呼ぶことはできません。

    ちなみに別スレッドで質問されているC#で構造体の参照渡しのコードが手続き型プログラミングの例です。データ構造を表すtest構造体とルーチンを含むhogeクラスに分かれてしまっています。おかげでhoge.foo()ルーチンがtest構造体の内部に直接干渉しているわけです。(カプセル化できていない)

    2011年11月15日 5:50
  • お二方のやり取をみていると関数が状態を持つという意味がおそらくすれ違っているような気がしますし、私も思い違いをしているかもしれませんので、具体的な例を出して確認させて下さい。例えば、

    class int Hoge()
    {
        private int a = 0;

        public int Fuga関数(int x)
       {
            return a += x;
        }
    }

    この時、Fuga関数は状態を持っているのか、持っていないのかということであり、呼び出す度に返ってくる値を意識しなければなりませんから状態を持つということであれば、上記はオブジェクト指向から外れているということになります。
    と、佐祐理さんの発言からは読み取れなくもないのですが(私の読解力が足らないかもしれませんが・・・)、おそらくそういうことではなく、関数内で状態を定義するためのフィールドやプロパティを定義する必要がない、いや、オブジェクト指向的に状態と関数はきっちりわけるものだから、そういうことをしてはいけないということなんだろうと思います。
    毎日が戦場さん的には、状態はオブジェクトに属するものだから、関数の内だろうが外だろうが問題ないと言われているのだと思います。
    要するに、オブジェクト指向において、一つのオブジェクト内で状態と関数を完全に分けて定義しなければならないのか、状態と関数をごちゃまぜにして定義しても良いのかという議論のような気がします。

    私の考えでは、基本的には状態と関数はきっちりと分けて定義すべきだと思いますが、それはオブジェクト指向を実現するための手法というかポリシーであって、広い意味でオブジェクトを捉えれば、関数と状態がごちゃまぜになっていても100%(100%ですよ)間違いとは言えないのかなぁという気がしてます。プロパティは使用者側から見れば状態のように見えますが、実は状態ではなくて関数です。プロパティは状態と関数をごちゃまぜにして定義していると言えなくもないのではないかと思います。要するにプロパティがある時点でごちゃまぜは間違いではないことが認められているのではないかと思いますし、それがオブジェクト指向から外れていると考えられていないのだと思います。
    以上の私の考えをまとめると、状態と関数はきっちり分けて定義すべきである。でも、分かれていないからといってオブジェクト指向から外れているとも言えないという、おそらく佐祐理さんと毎日が戦場さんの中間的な考えじゃないんだろうかという結論に今のところ至ってます。

    #勝手に結論まで持って行ってますが、想像も入っていますので、途中思い違いや誤りがあるかもしれません。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月15日 7:16
    モデレータ
  • お二方のやり取をみていると関数が状態を持つという意味がおそらくすれ違っているような気がしますし、私も思い違いをしているかもしれませんので、具体的な例を出して確認させて下さい。例えば、

    class int Hoge()
    {
        private int a = 0;

        public int Fuga関数(int x)
       {
            return a += x;
        }
    }

    この時、Fuga関数は状態を持っているのか、持っていないのかということであり、呼び出す度に返ってくる値を意識しなければなりませんから状態を持つということであれば、上記はオブジェクト指向から外れているということになります。

    構文が多少おかしいのは置いといて…そうではありません。

    このFuga関数を呼び出すには

    var hoge = new Hoge();
    hoge.Fuga関数(...);

    と書きます。状態を持っているのは「hoge.」つまりhogeオブジェクトでありFuga関数ではありません。このスレッドで話題に出てきた静的フィールド(private static int a = 0;)となったとしても状態を持っているのはHogeクラスでありFuga関数ではありません。

    ---追記

    var hoge1 = new Hoge();
    var hoge2 = new Hoge();
    hoge1.Fuga関数( 10 );
    hoge1.Fuga関数( 10 );
    hoge2.Fuga関数( 20 );
    hoge2.Fuga関数( 20 );

    と並べると状態を保持しているのが Fuga関数ではなく hoge1 や hoge2 であることが伝わりますでしょうか。

    • 編集済み 佐祐理 2011年11月15日 8:03
    2011年11月15日 7:59
  • 状態を持っているのは「hoge.」つまりhogeオブジェクトでありFuga関数ではありません。このスレッドで話題に出てきた静的フィールド(private static int a = 0;)となったとしても状態を持っているのはHogeクラスでありFuga関数ではありません。

    私もその認識でいます。私がわからないのは、佐祐理さんが「関数が状態を持つ」というのをどういう意味で使われているかということです。「関数が状態を持つ」とは、関数内にローカルなフィールドを定義できるという意味でしょうか?以前、佐祐理さんは、

    >関数呼び出しを超えて値を保持し続けることで、その関数を呼び出すたびに得られる結果が変わります。呼び出し側は関数の振る舞いがどのように変化するかを意識しながら呼び出す必要があり、それはカプセル化できていないことになります。

    と書かれていますが、これが「関数が状態を持つ」というのであれば、私が例に挙げたコードも「関数が状態を持つ」という状態なのか疑問に思ったのです。関数内にローカル変数を持とうが、関数外にローカル変数を持とうが、使用者側から見た関数の振る舞いは変わらないと思ったものですから。振る舞いが変わらないのであれば、オブジェクト指向として誤っているとは言えず、単に推薦されないということであり、C#はそれを認めないポリシーを採用したのではないかと思ったりもします。ひょっとして、C++やVBのように以前のソースの互換性という観点からではなく、純粋にそれを認めている言語もあったりしないだろうかと、ふと思ったりしています。まぁ、私もこういうのは気持ち悪いので無いかもしれませんが・・・。ただ、オブジェクト指向は状態と関数に分けて抽象化したものということが基本概念であれば、その状態と関数をきちんとブロックに分けて別々に定義しないからといって、オブジェクトの本質からは逸脱していないんだろうという気はしています。(何度も言いますが、こんなコードがあったとすれば、別々に分けて記述するように指導しますし、そういうポリシーを設けると思います。しかし、これは単にコードの見やすさや、デバッグのしやすさを考えてのことであり、オブジェクト指向から外れているからという理由で指導はしないであろうと何となく思っています。幸いC#ではそんなコードは書けないので、そういうケースは経験もありませんし、そういうことを想像したこともありませんから、何となくですが・・・)

    #私が例に出したコード、classの行に()要りませんね・・・。うっかりでした。ご指摘ありがとうございます。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月15日 14:10
    モデレータ
  • 先に挙げたrand()関数とRandomクラスを比較してみるとわかると思います。C言語では

    srand( 10 );
    int rand1 = rand();
    int rand2 = rand();
    srand( 20 );
    inr rand3 = rand();
    

    これに対してC#では

    var random1 = new Random( 10 );
    var rand1 = random1.Next();
    var rand2 = random1.Next();
    var random2 = new Random( 20 );
    var rand3 = random2.Next();
    
    // 状態がオブジェクトに保存されているので後からでもrandom1にアクセスできる
    var rand4 = random1.Next();
    

    このように対比すれば、乱数生成に必要なシードその他の情報が、関数に保持されているのかオブジェクトに保持されているのか、はっきりすると思います。

    rand()関数は別関数srand()で最後に指定されたシードを参照しますし、rand()自身、何回呼び出されたかの状態も保持しています。それらの情報に応じて次の乱数を返します。

    これに対してNext()メソッドはRandomオブジェクトの持つシード及び呼び出し回数に従って次の乱数を返します。つまり次の乱数を生成するために必要な情報は呼び出し側が「random1.」のように与えているわけでNext()メソッド内部の情報に依存しているわけではありません。

    srand()を新たに呼び出すとrand()の状態もリセットされますが、new Random()とコンストラクターを呼び出してもそれは別オブジェクトに関する処理なのでNext()メソッドは影響を受けません。

    # classの行にはintも余分だよもん

    2011年11月16日 0:37
  • あ~、何となくわかった気がします。佐祐理さんはインスタンスが別だとインスタンス毎に状態を保持しており、関数は別々の状態を返すから、関数が状態を保持していないとおっしゃっているんですね? 関数単独で状態を持てるのであれば、複数インスタンスでもC言語のrand()関数のように振る舞ってしまい、それは関数が状態を持っているので、オブジェクト指向から外れているということですね? 
    つまり、佐祐理さんは、「関数が状態と持つ」というのは、全てのインスタンスで状態を共有してしまうのでオブジェクト指向から外れているとおっしゃりたいと思っていますが、合っていますでしょうか? であれば問題となるのは関数内で状態が定義できるかどうかではなく、それは原因であって、その結果導かれるインスタンス間で状態を共有しているということが本質的な問題ということでしょうか? そうなると、静的フィールドを元に値を返すメソッドはどうなるのでしょうか?これは関数外で状態を保持していますが、関数が返す値はC言語のrando()関数のようにインスタンス共通になりますので、これも「関数が状態を持つ」ということになるのでしょうか? そうなるとやはり、状態を関数内で定義するか関数外で定義するかは「関数が状態を持つ」ということとは無関係ということになります。
    また、ふと気づいたのですが、次のような拡張メソッドを定義すればメソッド内に状態を持つことができます。以前出てきたC++と同じような意味になりますが、こんなことはオブジェクト指向から外れるとお考えになるのでしょうか? 私は外れていないと思います。理由は何度か述べていますが、状態はオブジェクトのどこで持ってもオブジェクト指向から外れているとは考えていないからです。だからとって状態をどこに定義してもOKということではなく、コーディング規約的には然るべきにところに定義すべきだと思います。

    static class RandomExtensions
    {
        static Random rnd = new Random();
    
        public static int rand(this Random s)
        {
            return rnd.Next();
        }
    }
    
    

    質問ばかりになってすみません。でも、この辺りの疑問が解決しないと何かすれ違っている気がしまして・・・。スレも長くなって重くなってきましたし(^^;

    ># classの行にはintも余分だよもん

    すみません(^^;


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月16日 3:12
    モデレータ
  • >佐祐理さん

    >データを伴わない関数単体をオブジェクトと呼ぶことはできません。

     

    多分、話のすれ違いはオブジェクトの定義の違いから来てるような気がします。

    私としては、オブジェクトとは「抽象的な何か」、「対象」、こういう意味で考えています。

    クラスのようなものは当然オブジェクトですし、intやfloatのような変数もオブジェクトだと考えています。

    また、関数もメモリ上の特定の領域のデータなわけですから当然関数という一つの枠組みのオブジェクトだと考えています。

     

    途中、ひらぽんさんが「オブジェクト指向プログラミングがいう「オブジェクト」とは 「責務を持つ実体」 という定義」という風に仰しゃっていましたが、どうもこのオブジェクト指向という言葉は人によって定義が微妙に違う気がします。

    私は、言葉は意思疎通の道具だと考えているので、特定の言葉の「正しい」定義というのは、その言葉を聞いたときに最も多くの人が共通認識を得られる意味だと思います。

    しかし、オブジェクト指向という言葉は新しい言葉なので、「正しい」と言える定義がまだ確定してない気がします。

     

    というわけで、ちょっと質問なのですが、皆さんオブジェクトというものをどの様に定義して考えてますか?

    また、オブジェクト指向プログラミングはどの様に定義されてますか?

    2011年11月16日 3:44
  • >毎日が戦場さんへ。

    スレッドが重くなってきたので、よろしくれば新しいスレッドを立てて続けませんか?

    #重たいのは私だけじゃないですよね? 投降後の表示は速いなぁ。 


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月16日 4:06
    モデレータ
  • OOP は、

    • カプセル化
    • 多態性(と、その手段の1つとしての継承)
    • メッセージ交換

    が基本だと思っています。メソッド呼び出しはメッセージ交換ですね。

    メッセージ交換に用いられる引数等のメッセージに伴う属性がオブジェクトであることはあっても、メッセージ交換そのものがオブジェクトであることはないと思っていますので、メソッド自身が状態を持ったオブジェクトであるという考え方は、私の思っている OOP には無いものです。(悪いものだとは思いませんが、設計としてはないものかな、という程度)

    (C# もそうですが) OOPL がメソッドの定義をオブジェクトとして扱えるようにしていることと、オブジェクトとして状態をもっていることは別であろうと思います。

     

    2011年11月16日 4:09
  • いろいろとおかしいと思います。

     

    var random1 = new Random(10);
    var random2 = new Random(20);
    var rand1 = random1.rand();
    var rand2 = random2.rand();
    

     

    未使用引数sがあるために、指定したオブジェクトと無関係な値が得られてしまいます。(rand1 / rand2はrandom1 / random2を使わずRandomExtensions.rndから生成されている。)

    とか。

    2011年11月16日 4:20
  • intやfloatのような変数もオブジェクトだと考えています。

    intやfloatは10とか3.0とか値だけを指してこう書かれたのだと思いますが、実際には値だけを持っているわけではありません。演算子も持っています。(演算子も関数の一部です。)
    intで1 / 3が0になり、floatで1.0 / 3.0が0.3333...になります。同じ値で同じ演算をしても、型によって結果が異なるのはこのためです。

    毎日が戦場さんがこの文脈で言われている「関数」はオブジェクトではありません。
    # この辺りはうまく説明できない…。関数ポインターはオブジェクトだけど、(非virtualな)メンバー関数も静的な存在とかそういう話をしたいところ。

    ただし、functor(ファンクタ;関数オブジェクト)と言って、関数をオブジェクトとして扱う考え方はあります。fempさんの挙げたラムダ式なんかがこれに含まれます。
    # rand()関数やRandomクラスを挙げながらC++ STLを挙げなかったのは<random>がfunctorだったから。 

    2011年11月16日 4:35
  • 未使用引数sがあるために、指定したオブジェクトと無関係な値が得られてしまいます。(rand1 / rand2はrandom1 / random2を使わずRandomExtensions.rndから生成されている。)

    とか。

    インスタンスと関係なくインスタンス共通の値を返すための拡張メソッドですから、sは未使用でかまいません。拡張メソッドという仕組みを利用してメソッド内で状態の定義を行いたかったので、未使用でもこのパラメータを定義しないわけにはいきませんからCA1801はOKです。CA1822を挙げられている理由はちょっとわかりませんでした。
    RandomExtensionsは実行テストを行っていますが、特にコンパイル時にワーニングは出ませんでしたよ。ちなみにパラメータがsなのは、stringクラスの拡張メソッドの例を雛形にしたからです。まぁ、sはそぐわないですね(^^;

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月16日 4:50
    モデレータ

  • また、関数もメモリ上の特定の領域のデータなわけですから当然関数という一つの枠組みのオブジェクトだと考えています。

     

    しかし、オブジェクト指向という言葉は新しい言葉なので、「正しい」と言える定義がまだ確定してない気がします。

    関数(メソッド)はオブジェクトの「要素」です。.Net はオブジェクトとして扱えますがそれは.Net の仕様でありオブジェクト指向の仕様ではありません。

    オブジェクト指向という言葉も人によって多少の解釈の違いはありますが、毎日が戦場さんが思っているほどに新しい言葉でもないし、曖昧な言葉でもありません。

    他の皆さんが一斉に「メソッドはオブジェクトではない」と反論しているように、毎日が戦場さんだけオブジェクト指向に対する考え方が一般と乖離しているように見受けられます。

    2011年11月16日 5:03
  • >trapemiyaさん

    確かにちょっと更新がが重くなってきましたね・・・^^;

    MSさんこんなにスレッド伸びること想定して設計しなかったのかしら。

     

    私も新しいスレッドを立てるのは良いと思うのですが、後で見た人には続いて方が解りやすいともいえます。

    他の方も移された方が良いようでしたら移そうかと思いますが、どうでしょう?

     

    後、新しくたてるとしたら、現状の議題はもはやC#のみに渡っているとは考えにくいのですがまた、C#フォーラムに建てて良いものでしょうか・・・?

     

    >K. Takaokaさん

    私としては、メソッドと状態を切り分けるのが正しいオブジェクト指向プログラミングだと言われても、じゃあclassというオブジェクトは関数も変数も持っているので切り分けてないんじゃ???と思ってしまうわけです。

     

    何となくですが、皆さんが仰しゃってるオブジェクト指向プログラミングは、関数が利用する全ての情報は関数内のみで完結するようにする。何か情報を与える場合は必ず引数として与え、グローバル変数のような関数自体が過去の情報を利用する、関数内のみで情報処理が完結しないことは許容しないということなのかなと思いました。

    なので多分、純粋なオブジェクト指向プログラミングになるとclassの中に関数と変数が存在することはなく、関数のみのクラスと、変数のみのクラスを用いてプログラミングするということになるということになるんでしょうか?

     

    だから、メンバ変数の存在自体オブジェクト指向の概念からは外れているのですが、実際問題メンバ変数がクラスにあった方が便利で、特に静的ローカル変数はメンバ変数を用いて実装できますから、利用者が静的ローカル変数として実装されているかメンバ変数として実装されているか判断する必要性を省くために静的ローカル変数を省いたということなんでしょうか?

     




    • 編集済み 毎日 2011年11月16日 5:14
    2011年11月16日 5:04
  • 後、新しくたてるとしたら、現状の議題はもはやC#のみに渡っているとは考えにくいのですがまた、C#フォーラムに建てて良いものでしょうか・・・?

    C#に限定した話題から外れることも多くなってきたので、「.NET Framework 全般」か、「アーキテクチャ概要」辺りだと思います。クラスやオブジェクトが話題の主体になっていくのであれば後者かな?
    蛇足かもしれませんが、新しいスレッドとこのスレッドへ行き来ができるように互いにリンクを掲載するのをお忘れなく。

    #いろいろ書きたいことはありますが、新しいスレッドで書くことにします。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年11月16日 5:27
    モデレータ
  • CAから始まるコード分析機能はPremium以上のエディションでサポートされています。(現実的には無理ですが)拡張メソッドではなくRandomクラスにrand()メソッドを定義した場合、CA1822が警告されるはずという意味で挙げました。

    未使用変数はいかなる場合でも設計が悪いと判断されるでしょう。拡張メソッドとして成立させるために、と主張されていますが、無関係なオブジェクトに対して拡張メソッドを定義すること自体おかしいです。
    # C++でとあるトリックのために使うのは見たことがありますが…。 

    拡張メソッドは、一般的に、必要な場合に限り注意して実装することをお勧めします。 クライアント コードで既存の型を拡張する必要がある場合、可能であれば既存の型から派生した新しい型を作成することで行ってください。

    とかいちいち引用しないといけないのでしょうか。

    public class TrapemiyaRandom: Random{
      static Random rnd = new Random();
      public int rand(){
        return rnd.Next();
      }
    }

    なら必然的にCA1801が対処され、代わりにCA1822が警告されます。そして警告に従い

    public class TrapemiyaRandom: Random{
      static Random rnd = new Random();
      public static int rand(){
        return rnd.Next();
      }
    }

    とするなら

    var random1 = new TrapemiyaRandom();
    random1.rand();

    という書き方はできません。

    2011年11月16日 5:31
  • しかし、オブジェクト指向という言葉は新しい言葉なので、「正しい」と言える定義がまだ確定してない気がします。

    ここだけ反応しますが、「オブジェクト指向」 はちっとも新しくないですよ。むしろ古い言葉です。
    ただし国内では理解できないエンジニアが多いのか、誤って理解されてるせいか、いまだに新しい概念と思ってる人も多いみたいですね。

    なおこの話題に関しては、さらに議論が膨らむ可能性があるため、みなさん仰るとおり別スレッドを起こされた方がいいでしょうね。


    ひらぽん http://d.hatena.ne.jp/hilapon/
    2011年11月16日 5:41
    モデレータ
  • こんにちは、毎日が戦場 さん。
     
    MSDN フォーラムのご利用ありがとうございます。オペレーターの山本です。

    みなさん感じていらっしゃるかと思いますが、こちらのスレッドはかなり長くなってきましたよね。
    #表示完了までに時間がかかるのでストレスがかかりますね。

    お手数ですが、新規にスレッドを立てて、こちらのスレッドのリンクを記載していただければと思います。
    #分割を試みたいのですが、もうどこで分割してよいやら判断できません。

    よろしくお願いいたします。
                                                                  
    日本マイクロソフト株式会社 フォーラム オペレーター 山本 春海


    2011年11月16日 5:43
  • >皆さん

    新しくスレッドを作りました。

    続きは下でお願いします。

     

    次スレ: オブジェクト指向は静的ローカル変数を許容するか?

    http://social.msdn.microsoft.com/Forums/ja-JP/architectureja/thread/03dec26d-d2fd-4058-94a3-1dc1acbf39cc

     

    2011年11月16日 5:52