Cの微妙な瞬間#

Cシャープ 機能の面で停滞の期間を経験している直接の競争相手であるJava言語とは異なり、C#が今日人気のある動的に開発されている言語であることは秘密ではありません。 Javaの議論の余地のない主な利点は、C#のような退屈で限定的なものではなく、真のクロスプラットフォームです。



C#はシンプルな言語です。そのシンプルさのおかげで、PHPも存在しています。 しかし同時に、それは非常に機能的であり、「ハイブリッド」言語のステータスを持ち、さまざまなパラダイムを組み合わせて、プログラミングの必須スタイルと機能的スタイルの両方の組み込みサポートを備えています。



他の言語と同様に、sharpeには独自の微妙さ、機能、「落とし穴」、あまり知られていない機能があります。 どういう意味ですか? カットの下を読んで......



梱包と開梱-誰もが知っているが、全員ではない



参照型(オブジェクト、動的、文字列、クラス、インターフェイス、デリゲート)はマネージヒープに格納され、値型(構造体、列挙、bool、バイト、char、int、float、double)はアプリケーションスタックに格納されます(値型がクラスフィールドです)。 値型を参照型に変換するには、暗黙的なボクシング操作が伴います。値型のコピーをラッパークラスに入れ、そのインスタンスをヒープに格納します。 パッケージングタイプはCLRによって生成され、格納された値タイプのインターフェイスを実装します。 参照型を値型に変換すると、 ボックス化解除操作が発生します。パッケージから値型のコピーを抽出し、スタックに配置します。

using System;



class Program

{

static void Main()

{

int val = 5;

object obj = val; //

int valUnboxed = ( int )obj; //

}

}








対応するILコード:



.locals init ([0] int32 val, [1] object obj, [2] int32 valUnboxed)

IL_0000: nop

IL_0001: ldc.i4.5

IL_0002: stloc.0

IL_0003: ldloc.0

IL_0004: box [mscorlib]System.Int32

IL_0009: stloc.1

IL_000a: ldloc.1

IL_000b: unbox.any [mscorlib]System.Int32

IL_0010: stloc.2

IL_0011: ret








パッケージ化と展開は比較的遅い操作であるため(コピーが必要)、可能な限り回避してください。 次のコードは、パッケージ化につながる非自明なケースを示しています。

using System;



class Program

{

static void Main()

{

// 1.

IComparable< int > iComp = 1;

// 2. enum System.Enum

Enum format = UriFormat.Unescaped;

// 3. dynamic

dynamic d = 1;

}

}








msdnでは、たとえば、非ユニバーサルコレクションクラス(ArrayList)など、値を何度もパックする必要がある場合は、値の型を避けることをお勧めします。 値型のラップは、ジェネリックコレクション(System.Collections.Generic名前空間)を使用することで回避できます。 ILコードレベルでのdynamicは同じオブジェクトであり、属性でタグ付けされている(常にではない)ことも覚えておく必要があります。



ラムダの再帰-悪意のある閉鎖について



ラムダ式を使用した再帰的階乗計算の古典的な実装に戻ります。

using System;

using System.Numerics;



class Program

{

static void Main()

{

Func< int , BigInteger> fact = null ;

fact = x => x > 1 ? x * fact(x - 1) : 1;

}

}








ラムダは、環境変数(クロージャ)をキャプチャする能力により、自身を参照できることを知っています。 キャプチャされたオブジェクトはラムダ式のコンテキスト外で変更できること、およびこの動作を言語でオーバーライドする組み込み機能はないことがわかっています。 ほとんどの場合、このアプローチは受け入れられないことは明らかであり、ラムダのコンテキスト外でキャプチャされた変数を変更する機能を何らかの方法で制限したいと思います。



一般的な場合、提示された問題は固定小数点コンビネーターを実装することで解決されます。

using System;

using System.Numerics;



class Program

{

static void Main()

{

var fact = YPointCombinator.Create< int , BigInteger>(f => (n) => n > 1 ? n * f(n - 1) : 1);

var power = YPointCombinator.Create< int , int , BigInteger>(f => (x, y) => y > 0 ? x * f(x, y - 1) : 1);

}

}

public static class YPointCombinator

{

public static Func<T1, T2> Create<T1, T2>(Func<Func<T1, T2>, Func<T1, T2>> f)

{

return f(r => Create( f )( r ));

}

public static Func<T1, T2, T3> Create<T1, T2, T3>(Func<Func<T1, T2, T3>, Func<T1, T2, T3>> f)

{

return f((r1, r2) => Create(f)(r1, r2));

}

}








プライベートフィールドとリフレクションフィールド、または私たちはあなたのOOPを吐き出します



リフレクションメカニズムを使用すると、クラスのプライベートフィールドの値を変更することもできます。

このビルドを緊急時にのみ適用し、カプセル化の原則を遵守することは明らかです。

using System;

using System.Reflection;



class Sample

{

private string _x = "No change me!" ;

public override string ToString()

{

return _x;

}

}

class Program

{

static void Main()

{

var sample = new Sample();

typeof (Sample).GetField( "_x" , BindingFlags.NonPublic | BindingFlags.Instance)

.SetValue(sample, "I change you..." );

Console .Write(sample);

Console .ReadKey();

}

}








UPD: 頭脳損傷で正しく指摘されているように、プライベートフィールドの変更は、アセンブリが必要なアクセス許可を持つコードグループに属している場合にのみ成功します。 次のようなクラス(メソッド)をマークすることにより、この権限を宣言的に要求できます。

[System.Security.Permissions.ReflectionPermission(System.Security.Permissions.SecurityAction.Assert)]







.NETセキュリティシステムではすべてが単純なわけではなく、.NET 4では大きな 変更が行われています



ダックタイピングとforeachループ



foreachを使用してクラスのインスタンスの要素を反復処理できるようにするには、GetEnumerator()メソッドを実装するだけで十分です。

using System;

using System.Collections.Generic;



class Sample

{

public IEnumerator< int > GetEnumerator()

{

for ( var i = 0; i < 10; ++i)

yield return i;

}

}

class Program

{

static void Main()

{

foreach ( var t in new Sample())

Console .WriteLine(t);

Console .ReadKey();

}

}








動的言語で一般的に使用される、いわゆる「アヒル」タイピングのこの小さな症状は、C#でも発生します。



匿名型-さらに可能です



匿名型の変数はコレクションに保存できます。 自分で見てください:

using System;

using System.Linq;



class Program

{

static void Main()

{

var list = new [] {

new { Name = "Alex" , Age = 18 },

new { Name = "Petr" , Age = 30 } }.ToList();



Console .Write(list.Find(x => x.Name == "Petr" ));

Console .ReadKey();

}

}








匿名型の変数は、別のスコープに渡すことができます。

using System;



class Program

{

static dynamic User

{

get { return new { Name = "Alex" , Age = 18 }; }

}



static void Main()

{

Console .Write(User.Name);

Console .ReadKey();

}

}








refは時々省略できます



C#4.0以降では、COM相互運用機能を介してメソッドを呼び出すときに、refキーワードを省略できます。 名前付き引数と組み合わせると、非常に印象的です。

using System;

using Word = Microsoft.Office.Interop.Word;



class Program

{

static void Main()

{

var app = new Word.Application();

Word.Document doc = null ;



// C# 2.0 - 3.5

object

filename = "test.doc" ,

visible = true ,

missing = Type.Missing;



doc = app.Documents.Open(

ref filename, ref missing, ref missing, ref missing, ref missing, ref missing,

ref missing, ref missing, ref missing, ref missing, ref missing, ref visible,

ref missing, ref missing, ref missing, ref missing);



// C# 4.0

doc = app.Documents.Open(FileName: "test.doc" , Visible: true );

}

}








注:名前付きパラメーターとrefを省略する機能は言語ツールであるため、.NET Framework 4.0と.NET Framework 2.0、3.0、3.5の両方を基本的なアプリケーションフレームワークとして選択できます。



舞台裏に残っているもの



言語の他のすべての「微妙さ」の中で、オブジェクトの決定論的な破壊の問題、ThreadAbortExceptionなどの非同期例外の処理の複雑さを取り上げます。 興味深いのは、強力なスレッド同期ツールと、言語での非同期操作のサポートの埋め込みに関連するC#5.0の今後の変更です。



All Articles