トップ «前の日記(2015-12-06) 最新 次の日記(2015-12-13)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2015-12-12

_ LINQの途中で列挙分を出力する

周回3つ分くらい遅れでLINQをいろいろ試しまくっているのだが、途中で列挙中の内容を見る方法を考え付いた(先日誰かも言っていたので普通に考え付くことらしいが、そこが周回遅れたるゆえんだ)のでメモ。
using System;
using System.Linq;
class Test
{
    static void Main()
    {
        var array = new int[] { 1, 2, 3, 4, 5 };
        Console.WriteLine(array
                  .Where(e => !(Console.WriteLine("elem:" + e) is object))
                  .Where((element, ind) => ind % 2 == 1)
                  .Sum()); 
    }
}

voidはobjectではないというのはおもしろい。ただコンパイル時にわかってしまうからwarning CS0184を喰らうのは嬉しくない。

次に以下のように変えると、LINQが単純に1つずつ関数呼び出しを処理しているのではないことがわかっておもしろい。

        Console.WriteLine(array
                  .Where(e => !(Console.WriteLine("elem:" + e) is object))
                  .Where((element, ind) => ind % 2 == 1)
                  .Where(e => !(Console.WriteLine("elem:" + e) is object))
                  .Sum()); 

単純に関数を1つずつ呼び出せば、elem:1, 2, 3, 4, 5と出力してからelme:2, 4と出そうなものだが、実際には

elem:1
elem:2
elem:2
elem:3
elem:4
elem:4
elem:5

となり、関数呼び出しを一気通貫している。余分な中間リストを作らないように最適化されているのだろう(というか、個々のメソッドは普通にメソッドとして定義されているのだからコンパイル時(かどうかはILを見ればわかるがまだ見てない)にばらしている(のかJITがいきなりそうしているのかは未確認)ということは、属性をうまく使っているということなのだろうか)(さらにJITが走ることを考えると、LINQは相当最適化されているのではなかろうか、というかされているだろう)

_ C#のVoid

なぜC#ではtypeof(void)が問題なく記述できるのだろうか。

でも、System.Voidと書くと、「System.Void は C# から使用できません。void 型オブジェクトを取得するには typeof(void) を使用してください」と怒り出す。

怒るので、上の例だとConsole.WriteLine("elem:" + e) is voidと素直に書くことができない。

しかも、var value = Console.WriteLine("elem:" + e);とも書けない(まあvoidだし)。書けないからtypeof(Console.WriteLine("elem:" + e))とも書けない(追記:いやtypeofはインスタンスには利用できないからそれは当然書けない)。

ではtypeof(void)は一体何の役に立つのだろうか?

というか、そこまでコンパイラでVoidを特別扱いしなくても良いような気がするんだよなぁ。(nullと区別がつかなくなって厄介なのかも知れないけど、JavaScriptですらundefinedとnullを区別したり区別しなかったり(仕様が無駄に複雑になっている気がしてきたから、そういうのを避けるためかな)している)

で、typeof(void)は何のためにあるんだろうか? (isやasがVoidを特別扱いで無視しているんだから、typeof(void)も無視で良いと思う)

ベルセルク コミック 1-37巻セット (ジェッツコミックス)(三浦建太郎)

_ 無駄な定数定義

asとisをいじくっているうちに、厳格なSIerが作りそうなヘッポコ定数定義クラスを思いついた。
namespace Great.SIer;
//たくさんのusing
public static class NullConstants
{
    public static readonly string NullString = null as string;
    public static readonly ArrayList NullArrayList = null as ArrayList;
    public static readonly List<string> NullStringList = null as List<string>;
    // あと5000行くらい続く
}

で、裸単騎のnullの利用は禁止で、必ず上から適切なnullの利用を強制する。

C#言語仕様の7.10.11 as演算子には次の記述がある。

型がdynamicではない場合、演算E as Tの結果は次の式と同じになります。
E is T ? (T)(E) : (T)null

だが、:の後ろの式の(T)は不要で、E is T ? (T)(E) : nullで問題なく動作はする。仕様書なので厳密に定義したのだろう。

これにヒントを得て、nullの代入に必ずキャストを必要とすると、相当な手間となる。

それで、上のような素晴らしく厳格な定数定義が生まれることになった。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|

ジェズイットを見習え