サーチ…
前書き
キーワードは、あらかじめ定義された予約済み識別子で、コンパイラにとって特別な意味を持ちます。 @
接頭辞を付けずにプログラムの識別子として使うことはできません。例えば@if
キーワード有効な識別子ではありませんif
。
備考
C#には、それぞれが特別な機能を持つ定義済みの "キーワード"(または予約語)のコレクションがあります。これらの単語は、 @
前に付けない限り、識別子(変数、メソッド、クラスなどの名前)として使用することはできません。
-
abstract
-
as
-
base
-
bool
-
break
-
byte
-
case
-
catch
-
char
-
checked
-
class
-
const
-
continue
-
decimal
-
default
-
delegate
-
do
-
double
-
else
-
enum
-
event
-
explicit
-
extern
-
false
-
finally
-
fixed
-
float
-
for
-
foreach
-
goto
-
if
-
implicit
-
in
-
int
-
interface
-
internal
-
is
-
lock
-
long
-
namespace
-
new
-
null
-
object
-
operator
-
out
-
override
-
params
-
private
-
protected
-
public
-
readonly
-
ref
-
return
-
sbyte
-
sealed
-
short
-
sizeof
-
stackalloc
-
static
-
string
-
struct
-
switch
-
this
-
throw
-
true
-
try
-
typeof
-
uint
-
ulong
-
unchecked
-
unsafe
-
ushort
-
using
(指示) -
using
(ステートメント) -
virtual
-
void
-
volatile
-
when
-
while
これらとは別に、C#はコード内で特定の意味を提供するためにいくつかのキーワードも使用します。それらをコンテキストキーワードといいます。コンテキストキーワードは識別子として使用することができ、識別子の前に@
付ける必要はありません。
stackalloc
stackalloc
キーワードは、スタック上にメモリの領域を作成し、そのメモリの先頭へのポインタを返します。スタックに割り当てられたメモリは、作成されたスコープが終了すると自動的に削除されます。
//Allocate 1024 bytes. This returns a pointer to the first byte.
byte* ptr = stackalloc byte[1024];
//Assign some values...
ptr[0] = 109;
ptr[1] = 13;
ptr[2] = 232;
...
安全でない状況で使用されます。
C#のすべてのポインタと同様に、読み取りと割り当ての境界を確認する境界はありません。割り当てられたメモリの範囲を超えて読み取ると、メモリ内の任意の場所にアクセスする可能性があり、アクセス違反の例外が発生する可能性があります。
//Allocate 1 byte
byte* ptr = stackalloc byte[1];
//Unpredictable results...
ptr[10] = 1;
ptr[-1] = 2;
スタックに割り当てられたメモリは、作成されたスコープが終了すると自動的に削除されます。これは、stackallocで作成されたメモリを返すことは絶対に避け、スコープの存続期間を超えて保存することはできません。
unsafe IntPtr Leak() {
//Allocate some memory on the stack
var ptr = stackalloc byte[1024];
//Return a pointer to that memory (this exits the scope of "Leak")
return new IntPtr(ptr);
}
unsafe void Bad() {
//ptr is now an invalid pointer, using it in any way will have
//unpredictable results. This is exactly the same as accessing beyond
//the bounds of the pointer.
var ptr = Leak();
}
stackalloc
は、変数の宣言と初期化時にのみ使用できます。以下は有効ではありません 。
byte* ptr;
...
ptr = stackalloc byte[1024];
備考:
stackalloc
は、パフォーマンスの最適化(計算またはinteropのいずれか)にのみ使用する必要があります。これは、次の事実によるものです。
- メモリがヒープではなくスタックに割り当てられているためガベージコレクタは不要です。変数が有効範囲外になるとすぐにメモリが解放されます
- ヒープではなくスタックにメモリを割り当てる方が速い
- データの局所性によるCPUのキャッシュヒットの可能性を高める
揮発性の
volatile
キーワードをフィールドに追加すると、フィールドの値が複数の別々のスレッドによって変更される可能性がコンパイラに示されます。 volatile
キーワードの主な目的は、シングルスレッドアクセスのみを想定したコンパイラの最適化を防止することです。 volatile
を使用すると、フィールドの値が使用可能な最新の値になり、その値は不揮発性の値であるキャッシングの対象にはなりません。
舞台裏での最適化による予期しない動作を防ぐために、複数のスレッドによって使用される可能性があるすべての変数をvolatile
としてマークすることをお勧めします。次のコードブロックを考えてみましょう。
public class Example
{
public int x;
public void DoStuff()
{
x = 5;
// the compiler will optimize this to y = 15
var y = x + 10;
/* the value of x will always be the current value, but y will always be "15" */
Debug.WriteLine("x = " + x + ", y = " + y);
}
}
上記のコードブロックでは、コンパイラは、文読み出しx = 5
とy = x + 10
の値と判断しy
いつものようにこのように、それが最後のステートメントを最適化する15ようにしてしまうy = 15
。しかし、変数x
は実際にはpublic
フィールドであり、 x
の値はこのフィールドで別々に動作する別のスレッドを通じて実行時に変更される可能性があります。この変更されたコードブロックを考えてみましょう。フィールドx
がvolatile
として宣言されていることに注意してください。
public class Example
{
public volatile int x;
public void DoStuff()
{
x = 5;
// the compiler no longer optimizes this statement
var y = x + 10;
/* the value of x and y will always be the correct values */
Debug.WriteLine("x = " + x + ", y = " + y);
}
}
現在、コンパイラはフィールドx
読み込み用途を探し、フィールドの現在の値が常に取得されるようにします。これにより、複数のスレッドがこのフィールドを読み書きしていても、常にx
の現在の値が取得されます。
volatile
は、 class
またはstruct
体内のフィールドでのみ使用できます。以下は有効ではありません 。
public void MyMethod() {volatileint x; }
volatile
は次の型のフィールドにしか適用できません:
- 参照型または既知の型として知られているジェネリック型パラメータ
-
sbyte
、byte
、short
、ushort
、int
、uint
、char
、float
、bool
などのプリミティブ型 -
byte
、sbyte
、short
、ushort
、int
またはuint
基づいて型をsbyte
-
IntPtr
とUIntPtr
備考:
-
volatile
修飾子は通常、lock文を使用してアクセスをシリアライズせずに複数のスレッドがアクセスするフィールドに使用されます。 -
volatile
キーワードは、参照型のフィールドに適用できます。 -
volatile
キーワードは、32ビットプラットフォーム上の64ビットプリミティブでは動作しません。以下のような連動操作Interlocked.Read
とInterlocked.Exchange
まだこれらのプラットフォームで安全なマルチスレッドアクセスに使用する必要があります。
一定
fixedステートメントは、メモリを1つの場所に固定します。メモリ内のオブジェクトは通常移動中ですので、ガベージコレクションが可能です。しかし、安全でないポインタをメモリアドレスに使用すると、そのメモリを移動してはいけません。
- ガベージコレクタが文字列データを再配置しないようにfixed文を使用します。
固定変数
var myStr = "Hello world!";
fixed (char* ptr = myStr)
{
// myStr is now fixed (won't be [re]moved by the Garbage Collector).
// We can now do something with ptr.
}
安全でない状況で使用されます。
固定配列サイズ
unsafe struct Example
{
public fixed byte SomeField[8];
public fixed char AnotherField[64];
}
fixed
はstruct
フィールドでのみ使用できます(安全でないコンテキストでも使用する必要があります)。
デフォルト
クラス、インタフェース、デリゲート、配列、nullable(intなど)およびポインタ型の場合、 default(TheType)
はnull
返しnull
。
class MyClass {}
Debug.Assert(default(MyClass) == null);
Debug.Assert(default(string) == null);
構造体と列挙型の場合、 default(TheType)
はnew TheType()
と同じ結果を返します。
struct Coordinates
{
public int X { get; set; }
public int Y { get; set; }
}
struct MyStruct
{
public string Name { get; set; }
public Coordinates Location { get; set; }
public Coordinates? SecondLocation { get; set; }
public TimeSpan Duration { get; set; }
}
var defaultStruct = default(MyStruct);
Debug.Assert(defaultStruct.Equals(new MyStruct()));
Debug.Assert(defaultStruct.Location.Equals(new Coordinates()));
Debug.Assert(defaultStruct.Location.X == 0);
Debug.Assert(defaultStruct.Location.Y == 0);
Debug.Assert(defaultStruct.SecondLocation == null);
Debug.Assert(defaultStruct.Name == null);
Debug.Assert(defaultStruct.Duration == TimeSpan.Zero);
default(T)
場合に特に有用であり得るT
全く制約がかどうかを決定するために存在していないいるため、一般的なパラメータでありT
例えば、参照型または値型です。
public T GetResourceOrDefault<T>(string resourceName)
{
if (ResourceExists(resourceName))
{
return (T)GetResource(resourceName);
}
else
{
return default(T);
}
}
読み取り専用
readonly
キーワードはフィールド修飾子です。フィールド宣言にreadonly
修飾子が含まれている場合、そのフィールドへの代入は宣言の一部として、または同じクラスのコンストラクターでのみ実行できます。
readonly
キーワードはconst
キーワードとは異なりreadonly
。 const
フィールドは、フィールドの宣言でのみ初期化できます。 readonly
フィールドは、宣言またはコンストラクタで初期化できます。したがって、 readonly
フィールドは、使用されるコンストラクタに応じて異なる値を持つことができreadonly
。
readonly
キーワードは、依存関係をreadonly
するときによく使われreadonly
。
class Person
{
readonly string _name;
readonly string _surname = "Surname";
Person(string name)
{
_name = name;
}
void ChangeName()
{
_name = "another name"; // Compile error
_surname = "another surname"; // Compile error
}
}
注意:フィールドを読み込み専用に宣言しても、 不変性は意味しません。フィールドが参照型の場合、オブジェクトの内容を変更することができます。通常、 Readonlyはオブジェクトを上書きしてそのオブジェクトのインスタンス化中にのみ割り当てるのを防ぐために使用されます。
注意:コンストラクタの内部では、readonlyフィールドを再割り当てすることができます
public class Car
{
public double Speed {get; set;}
}
//In code
private readonly Car car = new Car();
private void SomeMethod()
{
car.Speed = 100;
}
として
as
キーワードは、 キャストに似た演算子です。キャストできない場合は、使用しas
生成null
ではなく、その結果よりInvalidCastException
。
expression as type
はexpression as type
と同じexpression is type ? (type)expression : (type)null
警告でそのas
参照変換、NULL可能コンバージョン、ボクシング変換でのみ有効です。ユーザー定義の変換はサポートされていません 。代わりに通常のキャストを使用する必要があります。
上の展開のために、コンパイラは、 expression
が一度しか評価されず、単一の動的型チェックを使用するようなコードを生成します(上のサンプルの2つと異なります)。
いくつかの種類を容易にするための引数を期待するときに役立ちます。具体的にis
、キャストする前にすべての可能性をチェックするのではなく、例外をキャストしてキャッチするのでis
なく、複数のオプションをユーザーに付与します。オブジェクトをキャスト/チェックするときには 'as'を使用することがベストプラクティスであり、アンボックスペナルティが1つしか発生しません。使用is
確認すること、そしてキャストは2つのアンボクシングの罰則が発生します。
引数が特定の型のインスタンスであると予想される場合は、その目的が読者にとってより明確であるため、通常のキャストが優先されます。
as
呼び出すとnull
が生成さas
可能性があるため、常に結果をチェックしてNullReferenceException
を回避します。
使用例
object something = "Hello";
Console.WriteLine(something as string); //Hello
Console.Writeline(something as Nullable<int>); //null
Console.WriteLine(something as int?); //null
//This does NOT compile:
//destination type must be a reference type (or a nullable value type)
Console.WriteLine(something as int);
次のように使用as
ない同等の例:
Console.WriteLine(something is string ? (string)something : (string)null);
これは、カスタムクラスでEquals
関数をオーバーライドする場合に便利です。
class MyCustomClass
{
public override bool Equals(object obj)
{
MyCustomClass customObject = obj as MyCustomClass;
// if it is null it may be really null
// or it may be of a different type
if (Object.ReferenceEquals(null, customObject))
{
// If it is null then it is not equal to this instance.
return false;
}
// Other equality controls specific to class
}
}
は
オブジェクトが特定の型と互換性があるかどうか、つまりオブジェクトがBaseInterface
型のインスタンスか、 BaseInterface
から派生した型かどうかをBaseInterface
ます。
interface BaseInterface {}
class BaseClass : BaseInterface {}
class DerivedClass : BaseClass {}
var d = new DerivedClass();
Console.WriteLine(d is DerivedClass); // True
Console.WriteLine(d is BaseClass); // True
Console.WriteLine(d is BaseInterface); // True
Console.WriteLine(d is object); // True
Console.WriteLine(d is string); // False
var b = new BaseClass();
Console.WriteLine(b is DerivedClass); // False
Console.WriteLine(b is BaseClass); // True
Console.WriteLine(b is BaseInterface); // True
Console.WriteLine(b is object); // True
Console.WriteLine(b is string); // False
キャストの目的がオブジェクトを使用することである場合は、 as
キーワード '
interface BaseInterface {}
class BaseClass : BaseInterface {}
class DerivedClass : BaseClass {}
var d = new DerivedClass();
Console.WriteLine(d is DerivedClass); // True - valid use of 'is'
Console.WriteLine(d is BaseClass); // True - valid use of 'is'
if(d is BaseClass){
var castedD = (BaseClass)d;
castedD.Method(); // valid, but not best practice
}
var asD = d as BaseClass;
if(asD!=null){
asD.Method(); //prefered method since you incur only one unboxing penalty
}
しかし、C#7のpattern matching
機能では、is演算子を拡張して型をチェックし、同時に新しい変数を宣言します。 C#7の同じコード部分:
if(d is BaseClass asD ){
asD.Method();
}
タイプ
オブジェクトをインスタンス化する必要なく、オブジェクトのType
を返します。
Type type = typeof(string);
Console.WriteLine(type.FullName); //System.String
Console.WriteLine("Hello".GetType() == type); //True
Console.WriteLine("Hello".GetType() == typeof(string)); //True
const
const
は、プログラムの存続期間を通して決して変化しない値を表すために使用されます。その値は、実行時から一定の値を持つreadonly
キーワードとは対照的に、 コンパイル時から一定です。
たとえば、光の速度は決して変化しないので、定数に格納することができます。
const double c = 299792458; // Speed of light
double CalculateEnergy(double mass)
{
return mass * c * c;
}
これは、コンパイラがc
をその定数値で直接代入するので、本質的にはreturn mass * 299792458 * 299792458
を持つreturn mass * 299792458 * 299792458
と同じです。
その結果、宣言されたc
は変更できません。以下は、コンパイル時エラーを生成します:
const double c = 299792458; // Speed of light
c = 500; //compile-time error
定数には、メソッドと同じアクセス修飾子の接頭辞を付けることができます。
private const double c = 299792458;
public const double c = 299792458;
internal const double c = 299792458;
const
メンバーは性質上static
です。ただし、 static
明示的に使用することはできません。
メソッドローカル定数を定義することもできます。
double CalculateEnergy(double mass)
{
const c = 299792458;
return mass * c * c;
}
これらは、定義されたメソッドに対して暗黙的にローカルであるため、 private
またはpublic
キーワードの前に付けることはできません。
const
宣言ではすべての型を使用できるわけではありません。許容される値の型は、 sbyte
、 byte
、 short
、 ushort
、 int
、 uint
、 long
、 ulong
、 char
、 float
、 double
、 decimal
、 bool
、およびすべてのenum
型です。他の値型( TimeSpan
やGuid
)でconst
メンバーを宣言しようとすると、コンパイル時に失敗します。
特別な事前定義された参照型string
場合、定数は任意の値で宣言できます。他のすべての参照型では、定数を宣言できますが、常に値はnull
なければなりません。
const
値はコンパイル時に認識されるため、 switch
文のcase
ラベル、オプションのパラメータの標準引数、属性指定の引数などとして使用できます。
const
値が異なるアセンブリ間で使用される場合は、バージョン管理に注意する必要があります。たとえば、アセンブリAがpublic const int MaxRetries = 3;
定義する場合public const int MaxRetries = 3;
アセンブリBがその定数を使用する場合、アセンブリAで後でMaxRetries
の値を5
に変更した場合(再コンパイルした場合)、アセンブリBが再コンパイルされない限り 、その変更はアセンブリBでは有効になりませんAの新しいバージョンへの参照)。
そのため、値は、プログラムの将来の改訂で変更される可能性がある場合、その値は一般に公開する必要がある場合は、その値を宣言しないconst
あなたは何かが変更されるたびに、すべての依存アセンブリが再コンパイルされることを知っている場合を除きます。代わりに、実行時に解決されるconst
代わりにstatic readonly
を使用しています。
名前空間
namespace
キーワードは、コードベースの配置方法を理解するのに役立つ組織構成です。 C#の名前空間は、物理的なフォルダではなく、仮想空間です。
namespace StackOverflow
{
namespace Documentation
{
namespace CSharp.Keywords
{
public class Program
{
public static void Main()
{
Console.WriteLine(typeof(Program).Namespace);
//StackOverflow.Documentation.CSharp.Keywords
}
}
}
}
}
C#の名前空間は、連鎖構文で記述することもできます。以下は上記と同じです:
namespace StackOverflow.Documentation.CSharp.Keywords
{
public class Program
{
public static void Main()
{
Console.WriteLine(typeof(Program).Namespace);
//StackOverflow.Documentation.CSharp.Keywords
}
}
}
試してみて
try
、 catch
、 finally
、 throw
使用すると、コード内の例外を処理できます。
var processor = new InputProcessor();
// The code within the try block will be executed. If an exception occurs during execution of
// this code, execution will pass to the catch block corresponding to the exception type.
try
{
processor.Process(input);
}
// If a FormatException is thrown during the try block, then this catch block
// will be executed.
catch (FormatException ex)
{
// Throw is a keyword that will manually throw an exception, triggering any catch block that is
// waiting for that exception type.
throw new InvalidOperationException("Invalid input", ex);
}
// catch can be used to catch all or any specific exceptions. This catch block,
// with no type specified, catches any exception that hasn't already been caught
// in a prior catch block.
catch
{
LogUnexpectedException();
throw; // Re-throws the original exception.
}
// The finally block is executed after all try-catch blocks have been; either after the try has
// succeeded in running all commands or after all exceptions have been caught.
finally
{
processor.Dispose();
}
注意: return
キーワードはtry
ブロックで使用することができ、 finally
ブロックは実行中(返す直前)に実行されます。例えば:
try
{
connection.Open();
return connection.Get(query);
}
finally
{
connection.Close();
}
connection.Close()
の文は、 connection.Get(query)
結果が返される前に実行されconnection.Get(query)
。
持続する
すぐに、囲みループ構造の次の繰り返しに制御を渡す(for、foreach、do、while):
for (var i = 0; i < 10; i++)
{
if (i < 5)
{
continue;
}
Console.WriteLine(i);
}
出力:
5
6
7
8
9
var stuff = new [] {"a", "b", null, "c", "d"};
foreach (var s in stuff)
{
if (s == null)
{
continue;
}
Console.WriteLine(s);
}
出力:
a
b
c
d
ref、out
ref
キーワードとout
キーワードは、値ではなく参照渡しの引数になります。値型の場合、これは、変数の値が呼び出し先によって変更できることを意味します。
int x = 5;
ChangeX(ref x);
// The value of x could be different now
参照型の場合、変数のインスタンスは( ref
なしの場合のように)変更することはできませんが、置き換えることもできます:
Address a = new Address();
ChangeFieldInAddress(a);
// a will be the same instance as before, even if it is modified
CreateANewInstance(ref a);
// a could be an entirely new instance now
out
キーワードとref
キーワードの主な違いは、 ref
では呼び出し元によって変数が初期化される必要があり、 out
はその呼び出しを呼び出し先に渡すことです。
out
パラメーターを使用するには、メソッド定義と呼び出しメソッドの両方で明示的にout
キーワードを使用する必要があります。
int number = 1;
Console.WriteLine("Before AddByRef: " + number); // number = 1
AddOneByRef(ref number);
Console.WriteLine("After AddByRef: " + number); // number = 2
SetByOut(out number);
Console.WriteLine("After SetByOut: " + number); // number = 34
void AddOneByRef(ref int value)
{
value++;
}
void SetByOut(out int value)
{
value = 34;
}
out
パラメータは、メソッドが戻る前に割り当てられた値を持たなければならないので、コンパイルされません (代わりにref
を使用してコンパイルされます)。
void PrintByOut(out int value)
{
Console.WriteLine("Hello!");
}
Generic Modifierとしてキーワードを使用する
out
キーワードは、ジェネリックインターフェイスとデリゲートを定義するときに、ジェネリック型パラメータでも使用できます。この場合、 out
キーワードは、型パラメータが共変であることを指定します。
共分散を使用すると、汎用パラメーターで指定された型よりも派生型を使用することができます。これにより、バリアントインターフェイスを実装するクラスの暗黙的な変換と、デリゲート型の暗黙的な変換が可能になります。共分散と逆変分は参照型でサポートされていますが、値型ではサポートされていません。 - MSDN
//if we have an interface like this
interface ICovariant<out R> { }
//and two variables like
ICovariant<Object> iobj = new Sample<Object>();
ICovariant<String> istr = new Sample<String>();
// then the following statement is valid
// without the out keyword this would have thrown error
iobj = istr; // implicit conversion occurs here
チェックされている、チェックされていない
checked
unchecked
キーワードとunchecked
キーワードは、演算が数学的なオーバーフローをどのように処理するかを定義します。文脈における「オーバーフロー」 checked
し、 unchecked
キーワードがある場合に、ターゲット・データ・タイプを表すことができるよりも大きさが大きい値で整数演算の結果。
checked
ブロック内でオーバーフローが発生した場合(またはコンパイラがチェックされた算術をグローバルに使用するように設定されている場合)、望ましくない動作を警告する例外がスローされます。一方、 unchecked
れていunchecked
ブロックでは、オーバーフローは発生しません。例外はスローされず、値は単に反対の境界に折り返されます。これは微妙で、見つけにくいバグにつながる可能性があります。
ほとんどの算術演算はオーバーフローするほど大きくないか小さい値で行われるため、ほとんどの場合、ブロックをchecked
れたものとして明示的に定義する必要はありません。例えば、再帰関数で算術演算を行うときやユーザー入力を受けているときなど、オーバーフローを引き起こす可能性のある制限のない入力に対して算術演算を行うときは注意が必要です。
checked
もunchecked
checked
も浮動小数点演算に影響しません。
ブロックまたは式がunchecked
されていunchecked
と宣言された場合、その内部の算術演算はエラーを発生させることなくオーバーフローすることができます。この動作が望ましい例は、チェックサムの計算であり、計算中にその値が「ラップアラウンド」できるようになります。
byte Checksum(byte[] data) {
byte result = 0;
for (int i = 0; i < data.Length; i++) {
result = unchecked(result + data[i]); // unchecked expression
}
return result;
}
unchecked
されていunchecked
最も一般的な用途の1つは、チェックサムの一種であるobject.GetHashCode()
カスタムオーバーライドを実装することです。この質問に対する答えにキーワードの使用が表示されます。オーバーライドされたSystem.Object.GetHashCodeの最適なアルゴリズムは何ですか? 。
ブロックまたは式がchecked
されていると宣言された場合、オーバーフローを引き起こす算術演算の結果、 OverflowException
がスローされます。
int SafeSum(int x, int y) {
checked { // checked block
return x + y;
}
}
checkedとuncheckedの両方がブロックと式の形式である可能性があります。
チェックされたブロックとチェックされていないブロックは、呼び出されたメソッドには影響しません。たとえば、 Enum.ToObject()
、 Convert.ToInt32()
、およびユーザー定義の演算子は、カスタムのチェックされた/チェックされていないコンテキストの影響を受けません。
注意 :デフォルトのオーバーフローデフォルト動作(チェックと非チェック)は、 プロジェクトのプロパティまたは/ checked [+ | - ]コマンドラインスイッチで変更できます。デバッグビルドではチェックされた操作にデフォルト設定し、リリースビルドではチェックを外すことが一般的です。 checked
unchecked
キーワードとunchecked
キーワードは、 デフォルトのアプローチが適用されない場合にのみ使用され、正確性を保証するためには明示的な動作が必要です。
後藤
goto
は、ラベルで指定されたコード内の特定の行にジャンプするために使用できます。
aとしてのgoto
:
ラベル:
void InfiniteHello()
{
sayHello:
Console.WriteLine("Hello!");
goto sayHello;
}
ケースステートメント:
enum Permissions { Read, Write };
switch (GetRequestedPermission())
{
case Permissions.Read:
GrantReadAccess();
break;
case Permissions.Write:
GrantWriteAccess();
goto case Permissions.Read; //People with write access also get read
}
これは、C#がフォールスルーケースブロックをサポートしていないため、switch文で複数の動作を実行する場合に特に便利です。
例外の再試行
var exCount = 0;
retry:
try
{
//Do work
}
catch (IOException)
{
exCount++;
if (exCount < 3)
{
Thread.Sleep(100);
goto retry;
}
throw;
}
多くの言語と同様に、以下の場合を除いて、gotoキーワードの使用はお勧めしません。
C#に適用されるgoto
有効な使用法 :
switch文のfall-throughの場合
マルチレベルブレーク。代わりにLINQを使用することもできますが、通常はパフォーマンスが低下します。
アンラップされた低レベルのオブジェクトを扱うときのリソースの割り当て解除。 C#では、低レベルのオブジェクトは通常別のクラスにラップする必要があります。
有限状態機械、例えばパーサー;コンパイラによって生成された非同期/待機状態マシンを内部的に使用します。
列挙型
enum
キーワードは、明示的に継承することなく、このクラスが抽象クラスEnum
から継承することをコンパイラに伝えます。 Enum
はValueType
子孫であり、別の名前付き定数のセットで使用することを意図しています。
public enum DaysOfWeek
{
Monday,
Tuesday,
}
オプションで、それぞれの値(またはその一部)を指定することもできます。
public enum NotableYear
{
EndOfWwI = 1918;
EnfOfWwII = 1945,
}
この例では0の値を省略しましたが、これは通常悪い習慣です。 enum
は明示的な変換(YourEnumType) 0
によって生成されるデフォルト値を常に持ちます。ここでYourEnumType
は宣言されたenume
型です。 0の値が定義されていなければ、 enum
は開始時に定義された値を持ちません。
enum
のデフォルトの基になる型はint
です。基礎となる型をbyte
、 sbyte
、 short
、 ushort
、 int
、 uint
、 long
、 ulong
などの整数型に変更できます。以下は、基礎となる型byte
持つ列挙型です:
enum Days : byte
{
Sunday = 0,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};
また、キャストを使用して基本タイプとの間で変換することもできます。
int value = (int)NotableYear.EndOfWwI;
これらの理由から、ライブラリ関数を公開しているときにenum
が有効かどうかを常に確認するほうがよいでしょう。
void PrintNotes(NotableYear year)
{
if (!Enum.IsDefined(typeof(NotableYear), year))
throw InvalidEnumArgumentException("year", (int)year, typeof(NotableYear));
// ...
}
ベース
base
キーワードは、基本クラスのメンバーにアクセスするために使用されます。これは、仮想メソッドの基本実装を呼び出すか、呼び出すべき基本コンストラクタを指定するためによく使用されます。
コンストラクタの選択
public class Child : SomeBaseClass {
public Child() : base("some string for the base class")
{
}
}
public class SomeBaseClass {
public SomeBaseClass()
{
// new Child() will not call this constructor, as it does not have a parameter
}
public SomeBaseClass(string message)
{
// new Child() will use this base constructor because of the specified parameter in Child's constructor
Console.WriteLine(message);
}
}
仮想メソッドの基本実装を呼び出す
public override void SomeVirtualMethod() {
// Do something, then call base implementation
base.SomeVirtualMethod();
}
任意のメソッドから基本実装を呼び出すために、baseキーワードを使用することは可能です。これは、メソッド呼び出しを直接ベース実装に結びつけます。つまり、新しい子クラスが仮想メソッドをオーバーライドしても、基本実装が呼び出されるため、注意が必要です。
public class Parent
{
public virtual int VirtualMethod()
{
return 1;
}
}
public class Child : Parent
{
public override int VirtualMethod() {
return 11;
}
public int NormalMethod()
{
return base.VirtualMethod();
}
public void CallMethods()
{
Assert.AreEqual(11, VirtualMethod());
Assert.AreEqual(1, NormalMethod());
Assert.AreEqual(1, base.VirtualMethod());
}
}
public class GrandChild : Child
{
public override int VirtualMethod()
{
return 21;
}
public void CallAgain()
{
Assert.AreEqual(21, VirtualMethod());
Assert.AreEqual(11, base.VirtualMethod());
// Notice that the call to NormalMethod below still returns the value
// from the extreme base class even though the method has been overridden
// in the child class.
Assert.AreEqual(1, NormalMethod());
}
}
foreach
foreach
は、配列の要素またはIEnumerable
imを実装するコレクション内の項目を反復処理するために使用されます。
var lines = new string[] {
"Hello world!",
"How are you doing today?",
"Goodbye"
};
foreach (string line in lines)
{
Console.WriteLine(line);
}
これは出力されます
"こんにちは世界!"
"ご機嫌はいかがですか?"
"さようなら"
breakキーワードを使用してforeach
ループを終了するか、 continueキーワードを使用して次の繰り返しに進むことができます。
var numbers = new int[] {1, 2, 3, 4, 5, 6};
foreach (var number in numbers)
{
// Skip if 2
if (number == 2)
continue;
// Stop iteration if 5
if (number == 5)
break;
Console.Write(number + ", ");
}
// Prints: 1, 3, 4,
反復の順序は、配列やList
などの特定のコレクションに対してのみ保証されますが、他の多くのコレクションでは保証されません。
IEnumerable
は一般的に列挙可能なコレクションを示すために使用されますが、 foreach
は、コレクションがobject GetEnumerator()
メソッドを公開していることを要求し、 bool MoveNext()
メソッドとobject Current { get; }
プロパティ。
パラメータ
params
は、メソッドパラメータが可変数の引数を受け取ることを可能にします。つまり、0、1つまたは複数の引数がそのパラメータに対して許可されます。
static int AddAll(params int[] numbers)
{
int total = 0;
foreach (int number in numbers)
{
total += number;
}
return total;
}
このメソッドはint
引数の典型的なリスト、またはintの配列で呼び出すことができるようになりました。
AddAll(5, 10, 15, 20); // 50
AddAll(new int[] { 5, 10, 15, 20 }); // 50
params
は多くても一度現れなければならず、もし使用されるならば、後続の型が配列の型と異なっていても、引数リストの最後になければなりません。
params
キーワードを使用するときに関数のオーバーロードに注意してください。 C#は、より特定のオーバーロードとのマッチングを優先してから、 params
オーバーロードを使用しようとしています。たとえば、次の2つの方法があるとします。
static double Add(params double[] numbers)
{
Console.WriteLine("Add with array of doubles");
double total = 0.0;
foreach (double number in numbers)
{
total += number;
}
return total;
}
static int Add(int a, int b)
{
Console.WriteLine("Add with 2 ints");
return a + b;
}
次に、 params
オーバーロードを試す前に、特定の2引数のオーバーロードが優先されます。
Add(2, 3); //prints "Add with 2 ints"
Add(2, 3.0); //prints "Add with array of doubles" (doubles are not ints)
Add(2, 3, 4); //prints "Add with array of doubles" (no 3 argument overload)
ブレーク
ループ内で(for、foreach、do、while)、 break
文は最も内側のループの実行を中止し、その後ろのコードに戻ります。また、イテレータが終了したことを指定するyield
で使用することもできます。
for (var i = 0; i < 10; i++)
{
if (i == 5)
{
break;
}
Console.WriteLine("This will appear only 5 times, as the break will stop the loop.");
}
foreach (var stuff in stuffCollection)
{
if (stuff.SomeStringProp == null)
break;
// If stuff.SomeStringProp for any "stuff" is null, the loop is aborted.
Console.WriteLine(stuff.SomeStringProp);
}
break文は、caseまたはdefaultセグメントから脱出するためにswitch-caseコンストラクトでも使用されます。
switch(a)
{
case 5:
Console.WriteLine("a was 5!");
break;
default:
Console.WriteLine("a was something else!");
break;
}
switch文では、各case文の最後に 'break'キーワードが必要です。これは、シリーズの次のケース・ステートメントに「流入」することを可能にするいくつかの言語に反する。この問題を回避するには、 'goto'文を含めるか、 'case'文を順に積み重ねます。
次のコードは数字0, 1, 2, ..., 9
を与え、最後の行は実行されません。 yield break
は、関数の終了を意味します(単なるループではありません)。
public static IEnumerable<int> GetNumbers()
{
int i = 0;
while (true) {
if (i < 10) {
yield return i++;
} else {
yield break;
}
}
Console.WriteLine("This line will not be executed");
}
他の言語とは異なり、C#で特定の区切りにラベルを付ける方法はありません。つまり、ネストされたループの場合、最も内側のループだけが停止されます。
foreach (var outerItem in outerList)
{
foreach (var innerItem in innerList)
{
if (innerItem.ShoudBreakForWhateverReason)
// This will only break out of the inner loop, the outer will continue:
break;
}
}
ここで外側のループから脱出する場合は、次のようないくつかの戦略のうちの1つを使用できます。
- ループ構造全体から飛び出すためのgoto文。
- 外部ループの各繰り返しの最後にチェックできる特定のフラグ変数(次の例では
shouldBreak
)。 - コードをリファクタリングして、最も内側のループ本体で
return
文を使用するか、またはネストされたループ構造全体を完全に回避します。
bool shouldBreak = false;
while(comeCondition)
{
while(otherCondition)
{
if (conditionToBreak)
{
// Either tranfer control flow to the label below...
goto endAllLooping;
// OR use a flag, which can be checked in the outer loop:
shouldBreak = true;
}
}
if(shouldBreakNow)
{
break; // Break out of outer loop if flag was set to true
}
}
endAllLooping: // label from where control flow will continue
抽象
キーワードabstract
マークされたクラスはインスタンス化できません。
抽象メンバを含むクラス、または実装していない抽象メンバを継承するクラスは 、抽象クラスとしてマークする必要があります。抽象メンバが含まれていなくても、クラスは抽象クラスとしてマークされることがあります。
抽象クラスは、通常、実装の一部を別のコンポーネントで指定する必要がある場合に、基本クラスとして使用されます。
abstract class Animal
{
string Name { get; set; }
public abstract void MakeSound();
}
public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Meov meov");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Bark bark");
}
}
Animal cat = new Cat(); // Allowed due to Cat deriving from Animal
cat.MakeSound(); // will print out "Meov meov"
Animal dog = new Dog(); // Allowed due to Dog deriving from Animal
dog.MakeSound(); // will print out "Bark bark"
Animal animal = new Animal(); // Not allowed due to being an abstract class
キーワードabstract
マークされたメソッド、プロパティ、またはイベントは、そのメンバの実装がサブクラスで提供されることが期待されることを示します。上で述べたように、抽象メンバは抽象クラスでしか表示できません。
abstract class Animal
{
public abstract string Name { get; set; }
}
public class Cat : Animal
{
public override string Name { get; set; }
}
public class Dog : Animal
{
public override string Name { get; set; }
}
float、double、decimal
浮く
float
は.NETデータ型System.Single
エイリアスです。 IEEE 754単精度浮動小数点数を格納することができます。このデータ型は、 mscorlib.dll
存在しますmscorlib.dll
は、作成するときにすべてのC#プロジェクトによって暗黙的に参照されます。
3.4×10 38 -3.4×10 38:程度
小数精度:有効数字6〜9桁
表記法 :
float f = 0.1259;
var f1 = 0.7895f; // f is literal suffix to represent float values
float
型は、しばしば重大な丸め誤差を生じることに注意すべきである。精度が重要なアプリケーションでは、他のデータ型を考慮する必要があります。
ダブル
double
は.NETデータ型System.Double
エイリアスです。倍精度の64ビット浮動小数点数を表します。このデータ型は、C#プロジェクトで暗黙的に参照されるmscorlib.dll
存在します。
範囲:±5.0×10 -324〜 ±1.7×10 308
小数精度:有効数字15〜16桁
表記法 :
double distance = 200.34; // a double value
double salary = 245; // an integer implicitly type-casted to double value
var marks = 123.764D; // D is literal suffix to represent double values
小数点以下の
decimal
は、.NETデータ型System.Decimal
エイリアスです。キーワードは128ビットのデータ型を示します。浮動小数点型と比較して、小数点型は精度と範囲が小さく、財務および金銭計算に適しています。このデータ型は、C#プロジェクトで暗黙的に参照されるmscorlib.dll
存在します。
範囲:-7.9×10 28 7.9×10 28
小数精度:28〜29桁有効数字
表記法 :
decimal payable = 152.25m; // a decimal value
var marks = 754.24m; // m is literal suffix to represent decimal values
uint
符号なし整数 、またはuintは、正の整数のみを保持できる数値データ型です。名前のように、符号なし32ビット整数を表します。 uintキーワード自体は、Common Type SystemタイプのSystem.UInt32
別名です。このデータ型はmscorlib.dll
にありますmscorlib.dll
、作成するときにすべてのC#プロジェクトによって暗黙的に参照されます。 4バイトのメモリ空間を占有します。
符号なし整数は0から4,294,967,295までの任意の値を保持できます。
どのようにして符号なし整数を宣言しないかの例
uint i = 425697; // Valid expression, explicitly stated to compiler
var i1 = 789247U; // Valid expression, suffix allows compiler to determine datatype
uint x = 3.0; // Error, there is no implicit conversion
注意: Microsoftによると、 uintデータ型はCLSに準拠していないため、可能な限りintデータ型を使用することをお勧めします。
この
this
キーワードは、クラス(オブジェクト)の現在のインスタンスを参照します。そうすることで、クラス名(フィールド)とメソッドのパラメータ(またはローカル変数)の2つの変数を区別することができます。
public MyClass {
int a;
void set_a(int a)
{
//this.a refers to the variable defined outside of the method,
//while a refers to the passed parameter.
this.a = a;
}
}
キーワードの他の用途は、 非静的なコンストラクタのオーバーロードを連鎖させることです。
public MyClass(int arg) : this(arg, null)
{
}
public string this[int idx1, string idx2]
{
get { /* ... */ }
set { /* ... */ }
}
拡張メソッドを宣言する:
public static int Count<TItem>(this IEnumerable<TItem> source)
{
// ...
}
ローカル変数またはパラメータとの競合がない場合は、それを使用するかどうかのスタイルの問題でありthis
かどうか、そうthis.MemberOfType
とMemberOfType
、その場合に相当します。 base
キーワードも見てください。
現在のインスタンスで拡張メソッドを呼び出す場合は、 this
が必須であることに注意してください。たとえば、 IEnumerable<>
を実装しているクラスの静的でないメソッドの中にあり、前から拡張子Count
を呼び出す場合は、次のようにしなければなりません。
this.Count() // works like StaticClassForExtensionMethod.Count(this)
this
を省略することはできません。
にとって
構文: for (initializer; condition; iterator)
-
for
ループは、反復回数がわかっている場合によく使用されます。 - ループを開始する前に、
initializer
セクションのステートメントは一度だけ実行されます。 -
condition
セクションには、すべてのループ反復の終わりにループが終了するか、再度実行されるべきかを判断するために評価されるブール式が含まれています。 -
iterator
セクションは、ループ本体の各反復後に何が起こるかを定義します。
次の例は、 for
を文字列の文字に対して反復処理する方法for
示しています。
string str = "Hello";
for (int i = 0; i < str.Length; i++)
{
Console.WriteLine(str[i]);
}
出力:
H
e
l
l
o
for
ステートメントを定義するすべての式はオプションです。たとえば、次の文を使用して無限ループを作成します。
for( ; ; )
{
// Your code here
}
initializer
セクションには、同じタイプである限り、複数の変数を含めることができます。 condition
セクションは、 bool
評価できる任意の式で構成できます。 iterator
セクションでは、カンマで区切って複数のアクションを実行できます。
string hello = "hello";
for (int i = 0, j = 1, k = 9; i < 3 && k > 0; i++, hello += i) {
Console.WriteLine(hello);
}
出力:
こんにちは
hello1
hello12
while
while
演算子は、条件付きクエリがfalseに等しいか、またはコードがgoto
、 return
、 break
またはthrow
文で中断されるまで、コードブロックを反復処理します。
while
キーワードの構文:
while( 条件 ){ コードブロック。 }
例:
int i = 0;
while (i++ < 5)
{
Console.WriteLine("While is on loop number {0}.", i);
}
出力:
"whileループ番号1です。"
"whileループ番号2です。"
"whileループ番号3です。"
"whileループ番号4です。"
"whileループ番号5です。"
whileループは、囲まれたコードブロックの実行前に条件がチェックされるため、 Entry Controlledです。つまり、条件がfalseの場合、whileループはその文を実行しません。
bool a = false;
while (a == true)
{
Console.WriteLine("This will never be printed.");
}
ある条件でプロビジョニングせずにwhile
条件を指定すると、無限ループまたは無限ループになります。可能な限り、これは避けるべきですが、必要なときには例外的な状況が生じることがあります。
このようなループを作成するには、次のようにします。
while (true)
{
//...
}
C#コンパイラは、以下のようなループを変換することに注意してください。
while (true)
{
// ...
}
または
for(;;)
{
// ...
}
に
{
:label
// ...
goto label;
}
whileループは、ブール値(bool)を評価(返す)さえすれば、どんなに複雑なものであっても、条件を持ちうることに注意してください。ブール値を返す関数を含むこともできます(関数は `a == x 'などの式と同じ型に評価されるため)。例えば、
while (AgriculturalService.MoreCornToPick(myFarm.GetAddress()))
{
myFarm.PickCorn();
}
リターン
MSDN:returnステートメントは、表示されているメソッドの実行を終了し、呼び出し元のメソッドにコントロールを返します。オプションの値を返すこともできます。メソッドがvoid型の場合、return文は省略できます。
public int Sum(int valueA, int valueB)
{
return valueA + valueB;
}
public void Terminate(bool terminateEarly)
{
if (terminateEarly) return; // method returns to caller if true was passed in
else Console.WriteLine("Not early"); // prints only if terminateEarly was false
}
に
in
キーワードには3つの用途があります。
a) foreach
文の構文の一部として、またはLINQクエリの構文の一部として
foreach (var member in sequence)
{
// ...
}
b)ジェネリックインタフェースとジェネリックデリゲート型の文脈では、問題の型パラメータに対する反差異を意味する:
public interface IComparer<in T>
{
// ...
}
c)LINQクエリのコンテキストでは、クエリされているコレクションを参照します
var query = from x in source select new { x.Name, x.ID, };
を使用して
2種類がありますusing
、キーワードの使用をusing statement
してusing directive
:
usingステートメント :
using
キーワードは、IDisposable
インターフェイスを実装するIDisposable
が使用後に適切に処理されることを保証します。 usingステートメントには別のトピックがありますusingディレクティブ
using
ディレクティブには3つの使用法があります。usingディレクティブのmsdnページを参照してください 。 usingディレクティブには別のトピックがあります。
シールされた
sealed
修飾子は、クラスに適用すると、他のクラスが継承しないようにします。
class A { }
sealed class B : A { }
class C : B { } //error : Cannot derive from the sealed class
virtual
メソッド(または仮想プロパティ)に適用すると、 sealed
修飾子は、このメソッド(プロパティ)が派生クラスでオーバーライドされないようにします。
public class A
{
public sealed override string ToString() // Virtual method inherited from class Object
{
return "Do not override me!";
}
}
public class B: A
{
public override string ToString() // Compile time error
{
return "An attempt to override";
}
}
のサイズ
アンマネージ型のサイズをバイト単位で取得するために使用されます。
int byteSize = sizeof(byte) // 1
int sbyteSize = sizeof(sbyte) // 1
int shortSize = sizeof(short) // 2
int ushortSize = sizeof(ushort) // 2
int intSize = sizeof(int) // 4
int uintSize = sizeof(uint) // 4
int longSize = sizeof(long) // 8
int ulongSize = sizeof(ulong) // 8
int charSize = sizeof(char) // 2(Unicode)
int floatSize = sizeof(float) // 4
int doubleSize = sizeof(double) // 8
int decimalSize = sizeof(decimal) // 16
int boolSize = sizeof(bool) // 1
静的
static
修飾子は、静的メンバーを宣言するために使用されます。静的メンバーは、アクセスするためにインスタンス化する必要はありませんが、単にその名前、つまりDateTime.Now
介してアクセスします。
static
は、クラス、フィールド、メソッド、プロパティ、演算子、イベント、およびコンストラクタで使用できます。
クラスのインスタンスには、クラスのすべてのインスタンスフィールドの個別のコピーが含まれていますが、各静的フィールドのコピーは1つのみです。
class A
{
static public int count = 0;
public A()
{
count++;
}
}
class Program
{
static void Main(string[] args)
{
A a = new A();
A b = new A();
A c = new A();
Console.WriteLine(A.count); // 3
}
}
count
は、 A
クラスのインスタンスの総数に等しい。
静的修飾子は、クラスの静的コンストラクターを宣言したり、静的データを初期化したり、一度だけ呼び出す必要のあるコードを実行するためにも使用できます。静的コンストラクタは、クラスが初めて参照される前に呼び出されます。
class A
{
static public DateTime InitializationTime;
// Static constructor
static A()
{
InitializationTime = DateTime.Now;
// Guaranteed to only run once
Console.WriteLine(InitializationTime.ToString());
}
}
static class
はstatic
キーワードでマークされ、パラメータを処理する一連のメソッドの有益なコンテナとして使用できますが、必ずしもインスタンスに結び付ける必要はありません。クラスのstatic
性質のため、インスタンス化することはできませんが、 static constructor
含むことができます。 static class
一部の機能は次のとおりです。
- 継承することはできません
-
Object
以外のものから継承できない - インスタンスコンストラクタではなく静的コンストラクタを含むことができます
- 静的メンバーのみを含むことができます
- 封印されている
コンパイラはフレンドリーであり、クラス内にインスタンスメンバーが存在するかどうかを開発者に知らせます。たとえば、米国とカナダのメトリックを変換する静的クラスがあります。
static class ConversionHelper {
private static double oneGallonPerLitreRate = 0.264172;
public static double litreToGallonConversion(int litres) {
return litres * oneGallonPerLitreRate;
}
}
クラスが静的宣言されている場合:
public static class Functions
{
public static int Double(int value)
{
return value + value;
}
}
クラス内のすべての関数、プロパティ、またはメンバーも静的宣言する必要があります。クラスのインスタンスは作成できません。基本的に静的クラスを使用すると、論理的にグループ化された関数のバンドルを作成できます。
以来、C#6 static
も一緒に使用することができるusing
、静的メンバとメソッドをインポートします。それらはクラス名なしで使用することができます。
古い方法、 using static
をusing static
せずに:
using System;
public class ConsoleApplication
{
public static void Main()
{
Console.WriteLine("Hello World!"); //Writeline is method belonging to static class Console
}
}
using static
をusing static
例
using static System.Console;
public class ConsoleApplication
{
public static void Main()
{
WriteLine("Hello World!"); //Writeline is method belonging to static class Console
}
}
欠点
静的クラスは非常に便利ですが、独自の注意点があります:
静的クラスが呼び出されると、そのクラスはメモリにロードされ、静的クラスを格納するAppDomainがアンロードされるまで、ガベージコレクタを介して実行することはできません。
静的クラスはインタフェースを実装できません。
int
int
は、符号付き32ビット整数のデータ型であるSystem.Int32
のエイリアスです。このデータ型は、作成時にすべてのC#プロジェクトによって暗黙的に参照されるmscorlib.dll
あります。
範囲:-2,147,483,648〜2,147,483,647
int int1 = -10007;
var int2 = 2132012521;
長いです
longキーワードは、符号付き64ビット整数を表すために使用されます。 mscorlib.dll
に存在するSystem.Int64
データ型のエイリアスですmscorlib.dll
、作成するときにすべてのC#プロジェクトによって暗黙的に参照されます。
長い変数は、明示的にも暗黙的にも宣言できます。
long long1 = 9223372036854775806; // explicit declaration, long keyword used
var long2 = -9223372036854775806L; // implicit declaration, 'L' suffix used
長い変数は、-9,223,372,036,854,775,808から9,223,372,036,854,775,807までの任意の値を保持でき、変数が他の変数( int変数など)が保持できる範囲を超える値を保持する必要がある状況で役立ちます。
ulong
符号なし64ビット整数に使用されるキーワード。これは、 mscorlib.dll
あるSystem.UInt64
データ型を表していますmscorlib.dll
は、作成するときにすべてのC#プロジェクトによって暗黙的に参照されます。
範囲:0〜18,446,744,073,709,551,615
ulong veryLargeInt = 18446744073609451315;
var anotherVeryLargeInt = 15446744063609451315UL;
動的
dynamic
キーワードは、 動的に型指定されたオブジェクトで使用されます 。 dynamic
として宣言されたオブジェクトは、コンパイル時の静的検査に先行し、代わりに実行時に評価されます。
using System;
using System.Dynamic;
dynamic info = new ExpandoObject();
info.Id = 123;
info.Another = 456;
Console.WriteLine(info.Another);
// 456
Console.WriteLine(info.DoesntExist);
// Throws RuntimeBinderException
次の例では、デシリアライズされたJSONファイルからデータを簡単に読み取るために、NewtonsoftのライブラリJson.NETでdynamic
を使用しています。
try
{
string json = @"{ x : 10, y : ""ho""}";
dynamic deserializedJson = JsonConvert.DeserializeObject(json);
int x = deserializedJson.x;
string y = deserializedJson.y;
// int z = deserializedJson.z; // throws RuntimeBinderException
}
catch (RuntimeBinderException e)
{
// This exception is thrown when a property
// that wasn't assigned to a dynamic variable is used
}
動的キーワードにはいくつかの制限があります。それらの1つは拡張メソッドの使用です。次の例では、string: SayHello
拡張メソッドを追加します。
static class StringExtensions
{
public static string SayHello(this string s) => $"Hello {s}!";
}
最初のアプローチは、通常通り(文字列の場合と同じように)呼び出すことです。
var person = "Person";
Console.WriteLine(person.SayHello());
dynamic manager = "Manager";
Console.WriteLine(manager.SayHello()); // RuntimeBinderException
コンパイルエラーは発生しませんが、実行時にRuntimeBinderException
が発生します。これに対する回避策は、静的クラスを介して拡張メソッドを呼び出すことです。
var helloManager = StringExtensions.SayHello(manager);
Console.WriteLine(helloManager);
仮想、オーバーライド、新規
仮想と上書き
virtual
キーワードを使用すると、メソッド、プロパティ、インデクサまたはイベントを派生クラスでオーバーライドし、多態的な動作を表示できます。 (メンバーはC#ではデフォルトでは仮想ではありません)
public class BaseClass
{
public virtual void Foo()
{
Console.WriteLine("Foo from BaseClass");
}
}
メンバをオーバーライドするには、派生クラスでoverride
キーワードを使用します。 (メンバーの署名は同一でなければならないことに注意してください)
public class DerivedClass: BaseClass
{
public override void Foo()
{
Console.WriteLine("Foo from DerivedClass");
}
}
仮想メンバーの多態的な振る舞いは、呼び出されたときに、実行されている実際のメンバーがコンパイル時ではなく実行時に決定されることを意味します。特定のオブジェクトの最も派生したクラスの優先メンバーは、実行されるインスタンスになります。
要するに、オブジェクトはコンパイル時にBaseClass
型として宣言できますが、実行時にDerivedClass
インスタンスであれば、オーバーライドされたメンバが実行されます:
BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"
obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"
メソッドのオーバーライドはオプションです。
public class SecondDerivedClass: DerivedClass {}
var obj1 = new SecondDerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"
新しい
virtual
として定義されたメンバーだけがオーバーライド可能であり、多態性であるため、非仮想メンバーを再定義する派生クラスは予期しない結果を招く可能性があります。
public class BaseClass
{
public void Foo()
{
Console.WriteLine("Foo from BaseClass");
}
}
public class DerivedClass: BaseClass
{
public void Foo()
{
Console.WriteLine("Foo from DerivedClass");
}
}
BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"
obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too!
この場合、実行されたメンバは、オブジェクトの型に基づいてコンパイル時に常に決定されます。
- オブジェクトが
BaseClass
型の宣言されている場合(実行時に派生クラスであっても)、BaseClass
のメソッドが実行されます - オブジェクトが型の宣言されている場合
DerivedClass
、その後の方法DerivedClass
実行されます。
これは、通常、事故(同じタイプが派生タイプに追加された後に基本タイプにメンバーが追加された場合)であり、そのシナリオでコンパイル警告CS0108が生成されます。
それが意図的だった場合は、 new
キーワードを使用してコンパイラの警告を抑制します(そして、他の開発者にあなたの意図を知らせます)。振る舞いは変わらず、 new
キーワードは単にコンパイラの警告を抑制します。
public class BaseClass
{
public void Foo()
{
Console.WriteLine("Foo from BaseClass");
}
}
public class DerivedClass: BaseClass
{
public new void Foo()
{
Console.WriteLine("Foo from DerivedClass");
}
}
BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"
obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too!
上書きの使用はオプションではありません
C ++とは異なり、 override
キーワードの使用はオプションではありません 。
public class A
{
public virtual void Foo()
{
}
}
public class B : A
{
public void Foo() // Generates CS0108
{
}
}
ので、上記の例はまた、警告CS0108を引き起こすB.Foo()
自動的に上書きないA.Foo()
追加override
意図は基本クラスをオーバーライドして、多型の行動を起こす、追加するときにnew
静的な型を使用して電話を使用すると、非多型の挙動をしたいときに解決します。後者は、重大な混乱を招く可能性があるため、注意して使用する必要があります。
次のコードでもエラーが発生します。
public class A
{
public void Foo()
{
}
}
public class B : A
{
public override void Foo() // Error: Nothing to override
{
}
}
派生クラスは多型を導入することができます
次のコードは完全に有効です(まれですが)。
public class A
{
public void Foo()
{
Console.WriteLine("A");
}
}
public class B : A
{
public new virtual void Foo()
{
Console.WriteLine("B");
}
}
現在、B(およびその派生物)の静的参照を持つすべてのオブジェクトは多態性を使用してFoo()
を解決し、Aの参照はA.Foo()
使用します。
A a = new A();
a.Foo(); // Prints "A";
a = new B();
a.Foo(); // Prints "A";
B b = new B();
b.Foo(); // Prints "B";
仮想メソッドはプライベートにすることはできません
C#コンパイラは、無意味な構文を防ぐのに厳格です。 virtual
としてマークされたメソッドはプライベートにすることはできません。プライベートメソッドは派生型からは見えないため、上書きすることもできませんでした。これはコンパイルに失敗します:
public class A
{
private virtual void Foo() // Error: virtual methods cannot be private
{
}
}
非同期、待っている
await
キーワードは、Visual Studio 2012以降でサポートされているC#5.0リリースの一部として追加されました。マルチスレッド化を比較的容易にしたタスク並列ライブラリ(TPL)を活用しています。 async
キーワードとawait
キーワードは、次のように同じ関数でペアで使用されます。 await
キーワードは、待機中の非同期タスクが完了し、その結果が返されるまで、現在の非同期メソッドの実行を一時停止するために使用されます。 await
キーワードを使用するには、 await
キーワードを使用するメソッドにasync
キーワードを指定する必要があります。
async
とvoid
併用はお勧めしません。詳細はこちらをご覧ください 。
例:
public async Task DoSomethingAsync()
{
Console.WriteLine("Starting a useless process...");
Stopwatch stopwatch = Stopwatch.StartNew();
int delay = await UselessProcessAsync(1000);
stopwatch.Stop();
Console.WriteLine("A useless process took {0} milliseconds to execute.", stopwatch.ElapsedMilliseconds);
}
public async Task<int> UselessProcessAsync(int x)
{
await Task.Delay(x);
return x;
}
出力:
"役に立たないプロセスを始める..."
** ... 1秒遅れ... **「役に立たないプロセスは1000ミリ秒かかる。
Task
またはTask<T>
戻りメソッドが単一の非同期操作のみを返す場合、キーワードの組async
およびawait
は省略できます。
それよりむしろ:
public async Task PrintAndDelayAsync(string message, int delay)
{
Debug.WriteLine(message);
await Task.Delay(x);
}
これを行うことが好ましい。
public Task PrintAndDelayAsync(string message, int delay)
{
Debug.WriteLine(message);
return Task.Delay(x);
}
C#5.0では、 catch
とfinally
ではawait
を使用できません。
C#6.0では、 catch
とfinally
使用がawait
ます。
チャー
charは変数の中に格納された1文字です。これは、2バイトのメモリ空間を必要とするビルトインの値型です。これは、 mscorlib.dll
あるSystem.Char
データ型を表していますmscorlib.dll
は、作成時にすべてのC#プロジェクトによって暗黙的に参照されます。
これを行うには複数の方法があります。
-
char c = 'c';
-
char c = '\u0063'; //Unicode
-
char c = '\x0063'; //Hex
-
char c = (char)99;//Integral
charは暗黙的にushort, int, uint, long, ulong, float, double,
またはdecimal
変換され、そのcharの整数値が返されます。
ushort u = c;
99などを返す
ただし、他の型からcharへの暗黙的な変換はありません。代わりにそれらをキャストする必要があります。
ushort u = 99;
char c = (char)u;
ロック
lock
は、同じプロセス内の1つのスレッドだけがアクセスできるように、コードブロックに対してスレッドセーフティを提供します。例:
private static object _lockObj = new object();
static void Main(string[] args)
{
Task.Run(() => TaskWork());
Task.Run(() => TaskWork());
Task.Run(() => TaskWork());
Console.ReadKey();
}
private static void TaskWork()
{
lock(_lockObj)
{
Console.WriteLine("Entered");
Task.Delay(3000);
Console.WriteLine("Done Delaying");
// Access shared resources safely
Console.WriteLine("Leaving");
}
}
Output:
Entered
Done Delaying
Leaving
Entered
Done Delaying
Leaving
Entered
Done Delaying
Leaving
ユースケース:
同時に複数のスレッドで実行された場合、副作用を引き起こす可能性のあるコードブロックがある場合はいつでも。これを防ぐには、 共有同期オブジェクト (この例では_objLock
)とともにlockキーワードを使用できます。
_objLock
をnull
することはできません。また、コードを実行する複数のスレッドは、同じオブジェクトインスタンスを使用する必要があります( static
フィールドにするか、両方のスレッドで同じクラスインスタンスを使用する)
コンパイラー側から、lockキーワードは、 Monitor.Enter(_lockObj);
置き換えられる構文的な砂糖Monitor.Enter(_lockObj);
およびMonitor.Exit(_lockObj);
。したがって、これらの2つの方法でコードブロックを囲んでロックを置き換えると、同じ結果が得られます。あなたはC#の構文砂糖の実際のコードを見ることができます- ロックの例
ヌル
参照型の変数は、インスタンスへの有効な参照かヌル参照のいずれかを保持できます。 null参照は、参照可能な型変数のデフォルト値とnull値型です。
null
はnull参照を表すキーワードです。
式として、前述の型の変数にnull参照を割り当てることができます:
object a = null;
string b = null;
int? c = null;
List<int> d = null;
NULL値のない値の型にはnull参照を割り当てることはできません。次の割り当てはすべて無効です:
int a = null;
float b = null;
decimal c = null;
ヌル参照を、以下のようなさまざまな型の有効なインスタンスと混同しないでください。
- 空のリスト(
new List<int>()
) - 空の文字列(
""
) - 数字のゼロ(
0
、0f
、0m
) - ヌル文字(
'\0'
)
場合によっては、何かがnullか、空の/ defaultオブジェクトかどうかを確認することは意味があります。これを確認するにはSystem.String.IsNullOrEmpty(String)メソッドを使用するか、独自のメソッドを実装することができます。
private void GreetUser(string userName)
{
if (String.IsNullOrEmpty(userName))
{
//The method that called us either sent in an empty string, or they sent us a null reference. Either way, we need to report the problem.
throw new InvalidOperationException("userName may not be null or empty.");
}
else
{
//userName is acceptable.
Console.WriteLine("Hello, " + userName + "!");
}
}
内部
internal
キーワードは、型および型メンバーのアクセス修飾子です。内部型またはメンバーは、同じアセンブリ内のファイル内でのみアクセス可能です
使用法:
public class BaseClass
{
// Only accessible within the same assembly
internal static int x = 0;
}
アクセス修飾子
パブリック
型またはメンバには、同じアセンブリ内の他のコードまたはアセンブリを参照する別のアセンブリからアクセスできます。
プライベート
型またはメンバは、同じクラスまたは構造体のコードでのみアクセスできます。
保護された
型またはメンバにアクセスできるのは、同じクラスまたは構造体のコード、または派生クラスのみです。
内部
型またはメンバは、同じアセンブリ内の任意のコードでアクセスできますが、別のアセンブリからはアクセスできません。
保護された内部
型またはメンバには、同じアセンブリ内の任意のコード、または別のアセンブリ内の任意の派生クラスからアクセスできます。
アクセス修飾子が設定されていない場合は、デフォルトのアクセス修飾子が使用されます。だから、たとえそれが設定されていなくても、常に何らかの形のアクセス修飾子があります。
どこで
where
ではC#で2つの目的を果たすことができます:一般的な引数で型制約を設定し、LINQクエリをフィルタリングします。
一般的なクラスでは、
public class Cup<T>
{
// ...
}
Tは型パラメータと呼ばれます。クラス定義は、Tに供給可能な実際の型に制約を課すことができます。
次の種類の制約を適用できます。
- 値の型
- 参照型
- デフォルトコンストラクタ
- 継承と実装
値の型
この場合、 struct
s(これにはint
、 boolean
などの 'プリミティブ'データ型が含まれています)のみを指定できます
public class Cup<T> where T : struct
{
// ...
}
参照型
この場合、クラス・タイプのみを提供することができます
public class Cup<T> where T : class
{
// ...
}
ハイブリッド値/参照型
型引数をデータベースで使用可能なものに制限することが望ましい場合があります。これらの型引数は通常、値型と文字列にマップされます。すべての型の制限が満たされなければならないためwhere T : struct or string
(これは有効な構文ではありません)を指定することはできません。この問題を回避するには、タイプ引数を "...ブール型、SByte型、バイト型、Int16、UInt16、Int32、UInt32、Int64、UInt64、Single、Double、Decimal、DateTime、Char、およびString"の型を持つIConvertible
に制限します。 "これは実際にはまれですが、他のオブジェクトがIConvertibleを実装する可能性があります。
public class Cup<T> where T : IConvertible
{
// ...
}
デフォルトコンストラクタ
デフォルトのコンストラクタを含む型だけが許可されます。これには、デフォルト(パラメータなし)のコンストラクタを含む値の型とクラスが含まれます
public class Cup<T> where T : new
{
// ...
}
継承と実装
特定の基本クラスを継承したり、特定のインタフェースを実装している型だけを提供することができます。
public class Cup<T> where T : Beverage
{
// ...
}
public class Cup<T> where T : IBeer
{
// ...
}
制約は別の型パラメータを参照することさえできます:
public class Cup<T, U> where U : T
{
// ...
}
型引数には複数の制約を指定できます。
public class Cup<T> where T : class, new()
{
// ...
}
前の例では、クラス定義のジェネリック制約を示していますが、型引数が指定されている場所であれば、クラス、構造体、インタフェース、メソッドなど、どこでも制約を使用できます。
where
にもLINQ節することができます。この場合は、SQLのWHERE
と似ています。
int[] nums = { 5, 2, 1, 3, 9, 8, 6, 7, 2, 0 };
var query =
from num in nums
where num < 5
select num;
foreach (var n in query)
{
Console.Write(n + " ");
}
// prints 2 1 3 2 0
extern
extern
キーワードは、外部で実装されたメソッドを宣言するために使用されます。これをDllImport属性と組み合わせて使用すると、Interopサービスを使用してアンマネージコードを呼び出すことができます。この場合はstatic
修飾子が付いてくるでしょう
例えば:
using System.Runtime.InteropServices;
public class MyClass
{
[DllImport("User32.dll")]
private static extern int SetForegroundWindow(IntPtr point);
public void ActivateProcessWindow(Process p)
{
SetForegroundWindow(p.MainWindowHandle);
}
}
これは、User32.dllライブラリからインポートされたSetForegroundWindowメソッドを使用します。
これは、外部アセンブリエイリアスを定義するためにも使用できます。単一のアセンブリから同じコンポーネントの異なるバージョンを参照することができます。
同じ完全修飾型名を持つ2つのアセンブリを参照するには、次のようにコマンドプロンプトでエイリアスを指定する必要があります。
/r:GridV1=grid.dll
/r:GridV2=grid20.dll
これにより、外部エイリアスGridV1とGridV2が作成されます。これらのエイリアスをプログラム内で使用するには、externキーワードを使用して参照してください。例えば:
extern alias GridV1;
extern alias GridV2;
ブール
ブール値true
およびfalse
を格納するためのキーワード。 boolはSystem.Booleanのエイリアスです。
boolのデフォルト値はfalseです。
bool b; // default value is false
b = true; // true
b = ((5 + 2) == 6); // false
boolがnull値を許可するには、boolとして初期化する必要があります。
boolのデフォルト値は?無効です。
bool? a // default value is null
いつ
when
はC#6で追加されたキーワードで、例外フィルタリングに使用されます。
when
キーワードを導入する前に、例外の種類ごとに1つのcatch節を持つことができました。キーワードの追加により、より細かい制御が可能になりました。
when
式はに添付されたcatch
支店、および場合にのみwhen
の条件があるtrue
、 catch
句が実行されます。同じ例外クラスタイプを持つ複数のcatch
節を持つことができwhen
条件が異なるwhen
異なる可能性when
ます。
private void CatchException(Action action)
{
try
{
action.Invoke();
}
// exception filter
catch (Exception ex) when (ex.Message.Contains("when"))
{
Console.WriteLine("Caught an exception with when");
}
catch (Exception ex)
{
Console.WriteLine("Caught an exception without when");
}
}
private void Method1() { throw new Exception("message for exception with when"); }
private void Method2() { throw new Exception("message for general exception"); }
CatchException(Method1);
CatchException(Method2);
チェックされていない
unchecked
キーワードは、コンパイラーがオーバーフロー/アンダーフローをチェックするのを防ぎます。
例えば:
const int ConstantMax = int.MaxValue;
unchecked
{
int1 = 2147483647 + 10;
}
int1 = unchecked(ConstantMax + 10);
unchecked
キーワードがなければ、2つの加算演算のどちらもコンパイルされません。
これはいつ便利ですか?
これは、オーバーフローのチェックに時間がかかるため、またはオーバーフロー/アンダーフローが望ましい動作(たとえば、ハッシュコードの生成時)になったときに、オーバーフローしない計算を高速化するのに役立ちます。
無効
予約語"void"
は、 System.Void
型のエイリアスでSystem.Void
、2つの用途があります。
- 戻り値を持たないメソッドを宣言します。
public void DoSomething()
{
// Do some work, don't return any value to the caller.
}
戻り値の型がvoidのメソッドは、その本文にreturn
キーワードを持つことができます。これは、メソッドの実行を終了し、呼び出し元にフローを返す場合に便利です。
public void DoSomething()
{
// Do some work...
if (condition)
return;
// Do some more work if the condition evaluated to false.
}
- 安全でないコンテキストの不明な型へのポインタを宣言します。
安全でないコンテキストでは、型はポインタ型、値型、参照型のいずれかです。ポインタ型宣言は通常type* identifier
であり、型は既知の型( int* myInt
)ですが、型が不明なvoid* identifier
でもint* myInt
ん。
voidポインター型の宣言は、マイクロソフトによって推奨されません。
if、if ... else、if ... else if
if
文は、プログラムの流れを制御するために使用されます。 if
ステートメントは、 Boolean
式の値に基づいて実行するステートメントを識別します。
単一のステートメントの場合、 braces
{}はオプションですが推奨されています。
int a = 4;
if(a % 2 == 0)
{
Console.WriteLine("a contains an even number");
}
// output: "a contains an even number"
if
はelse
節もあり、条件がfalseと評価された場合に実行されます:
int a = 5;
if(a % 2 == 0)
{
Console.WriteLine("a contains an even number");
}
else
{
Console.WriteLine("a contains an odd number");
}
// output: "a contains an odd number"
if
... else if
構文を使用すると、複数の条件を指定できます。
int a = 9;
if(a % 2 == 0)
{
Console.WriteLine("a contains an even number");
}
else if(a % 3 == 0)
{
Console.WriteLine("a contains an odd number that is a multiple of 3");
}
else
{
Console.WriteLine("a contains an odd number");
}
// output: "a contains an odd number that is a multiple of 3"
条件は上記の例で満たされている場合、コントロールは他のテストをスキップして... else構造の場合場合は、使用している場合、他のconstruct.Soは、テストの順序が重要な場合は、その特定の終わりにジャンプしますことに注意することが重要
C#ブール式は短絡評価を使用します。これは、評価条件が副作用を伴う場合に重要です。
if (someBooleanMethodWithSideEffects() && someOtherBooleanMethodWithSideEffects()) {
//...
}
someOtherBooleanMethodWithSideEffects
が実際に実行されるという保証はありません。
以前の条件が後のものを評価することが「安全」であることを保証する場合にも重要です。例えば:
if (someCollection != null && someCollection.Count > 0) {
// ..
}
注文を取り消す場合、注文は非常に重要です。
if (someCollection.Count > 0 && someCollection != null) {
someCollection
がnull
場合、 NullReferenceException
がスローされnull
。
行う
do演算子は、条件付きクエリがfalseに等しくなるまで、コードのブロックを反復処理します。 do-whileループは、 goto
、 return
、 break
またはthrow
文によって中断することもできます。
do
キーワードの構文は次のとおりです。
do { コードブロック; } while( 条件 );
例:
int i = 0;
do
{
Console.WriteLine("Do is on loop number {0}.", i);
} while (i++ < 5);
出力:
「Doはループ番号1にあります。
「Doはループ番号2にあります。
「Doはループ番号3にあります。
「Doはループ番号4にあります。
「Doはループ番号5にあります。
while
ループとは異なり、do-whileループはExit Controlledです。つまり、条件が初めて失敗した場合でも、do-whileループは少なくとも1回ステートメントを実行します。
bool a = false;
do
{
Console.WriteLine("This will be printed once, even if a is false.");
} while (a == true);
オペレーター
組み込み演算子 (変換演算子を含む)の大半は、 operator
キーワードとpublic
修飾子およびstatic
修飾子を使用してオーバーロードすることができます。
演算子には単項演算子、2項演算子、変換演算子の3つの形式があります。
単項演算子および二項演算子は、包含型と同じ型の少なくとも1つのパラメータを必要とし、また、あるものは相補型の一致演算子を必要とします。
変換演算子は、囲みタイプとの間で変換する必要があります。
public struct Vector32
{
public Vector32(int x, int y)
{
X = x;
Y = y;
}
public int X { get; }
public int Y { get; }
public static bool operator ==(Vector32 left, Vector32 right)
=> left.X == right.X && left.Y == right.Y;
public static bool operator !=(Vector32 left, Vector32 right)
=> !(left == right);
public static Vector32 operator +(Vector32 left, Vector32 right)
=> new Vector32(left.X + right.X, left.Y + right.Y);
public static Vector32 operator +(Vector32 left, int right)
=> new Vector32(left.X + right, left.Y + right);
public static Vector32 operator +(int left, Vector32 right)
=> right + left;
public static Vector32 operator -(Vector32 left, Vector32 right)
=> new Vector32(left.X - right.X, left.Y - right.Y);
public static Vector32 operator -(Vector32 left, int right)
=> new Vector32(left.X - right, left.Y - right);
public static Vector32 operator -(int left, Vector32 right)
=> right - left;
public static implicit operator Vector64(Vector32 vector)
=> new Vector64(vector.X, vector.Y);
public override string ToString() => $"{{{X}, {Y}}}";
}
public struct Vector64
{
public Vector64(long x, long y)
{
X = x;
Y = y;
}
public long X { get; }
public long Y { get; }
public override string ToString() => $"{{{X}, {Y}}}";
}
例
var vector1 = new Vector32(15, 39);
var vector2 = new Vector32(87, 64);
Console.WriteLine(vector1 == vector2); // false
Console.WriteLine(vector1 != vector2); // true
Console.WriteLine(vector1 + vector2); // {102, 103}
Console.WriteLine(vector1 - vector2); // {-72, -25}
構造体
struct
型は、通常、矩形の座標や在庫内の項目の特性など、関連する変数の小さなグループをカプセル化するために使用される値型です。
クラスは参照型、構造体は値型です。
using static System.Console;
namespace ConsoleApplication1
{
struct Point
{
public int X;
public int Y;
public override string ToString()
{
return $"X = {X}, Y = {Y}";
}
public void Display(string name)
{
WriteLine(name + ": " + ToString());
}
}
class Program
{
static void Main()
{
var point1 = new Point {X = 10, Y = 20};
// it's not a reference but value type
var point2 = point1;
point2.X = 777;
point2.Y = 888;
point1.Display(nameof(point1)); // point1: X = 10, Y = 20
point2.Display(nameof(point2)); // point2: X = 777, Y = 888
ReadKey();
}
}
}
構造体にはコンストラクタ、定数、フィールド、メソッド、プロパティ、インデクサ、演算子、イベント、ネストされた型が含まれることがありますが、そのようなメンバがいくつか必要な場合は、
構造体をいつ使うべきか、そしてクラスをいつ使うべきかについてのMSからのいくつかの提案 :
考慮する
型のインスタンスが小さく、一般的に短命である場合、または一般に他のオブジェクトに埋め込まれている場合は、クラスの代わりに構造体を定義します。
避ける
型が次のすべての特性を持たない限り、構造体を定義します。
- これは論理的に単一の値を表し、プリミティブ型(int、doubleなど)に似ています。
- インスタンスサイズは16バイト未満です。
- それは不変です。
- 頻繁にボックスに入れる必要はありません。
スイッチ
switch
ステートメントは、候補のリストから実行するスイッチセクションを選択するコントロールステートメントです。 switchステートメントには、1つまたは複数のスイッチセクションが含まれます。各スイッチセクションには、1つ以上のcase
ラベルの後に1つ以上のステートメントが続きます。大文字小文字のラベルに一致する値が含まれていない場合は、 default
セクションに制御が移りdefault
存在する場合)。ケースフォールスルーは、C#では厳密にはサポートされていません。ただし、1つ以上のcase
ラベルが空のcase
、コードを含む次のcase
ブロックのコードに従います。これにより、同じ実装で複数のcase
ラベルをグループ化できます。次の例では、 month
が12のcase
、 case
ラベル12
1
と2
がグループ化されているため、 case
case 2
のコードが実行されます。 case
ブロックが空でないcase
、次のcase
ラベルの前にbreak
が存在する必要があります。そうでない場合、コンパイラはエラーにフラグを付けます。
int month = DateTime.Now.Month; // this is expected to be 1-12 for Jan-Dec
switch (month)
{
case 12:
case 1:
case 2:
Console.WriteLine("Winter");
break;
case 3:
case 4:
case 5:
Console.WriteLine("Spring");
break;
case 6:
case 7:
case 8:
Console.WriteLine("Summer");
break;
case 9:
case 10:
case 11:
Console.WriteLine("Autumn");
break;
default:
Console.WriteLine("Incorrect month index");
break;
}
case
はコンパイル時に知られている値(例えば1
、 "str"
、 Enum.A
)でしかラベル付けできないので、 variable
は有効なcase
ラベルではありませんが、 const
またはEnum
値は(anyリテラル値)。
インタフェース
interface
は、メソッド、プロパティ、およびイベントのシグネチャが含まれています。派生クラスは、メンバーの宣言のみがインターフェイスに含まれるため、メンバーを定義します。
インタフェースは、 interface
キーワードを使用して宣言されinterface
。
interface IProduct
{
decimal Price { get; }
}
class Product : IProduct
{
const decimal vat = 0.2M;
public Product(decimal price)
{
_price = price;
}
private decimal _price;
public decimal Price { get { return _price * (1 + vat); } }
}
安全でない
unsafe
キーワードは、型宣言やメソッド宣言、またはインラインブロックの宣言に使用できます。
このキーワードの目的は、問題のブロックに対してC#の安全でないサブセットを使用できるようにすることです。安全でないサブセットには、ポインタ、スタック割り当て、Cのような配列などの機能が含まれています。
安全でないコードは検証可能ではないため、その使用法は推奨されません。安全でないコードをコンパイルするには、スイッチをC#コンパイラに渡す必要があります。さらに、CLRでは、実行中のアセンブリに完全な信頼が必要です。
これらの制限にもかかわらず、安全でないコードは、いくつかの操作をより効果的にする(例えば、配列インデックス付け)か、より簡単にする(例えば、いくつかのアンマネージドライブラリを持つinterop)有効な使用法を持っています。
非常に簡単な例として
// compile with /unsafe
class UnsafeTest
{
unsafe static void SquarePtrParam(int* p)
{
*p *= *p; // the '*' dereferences the pointer.
//Since we passed in "the address of i", this becomes "i *= i"
}
unsafe static void Main()
{
int i = 5;
// Unsafe method: uses address-of operator (&):
SquarePtrParam(&i); // "&i" means "the address of i". The behavior is similar to "ref i"
Console.WriteLine(i); // Output: 25
}
}
ポインタを使って作業しているときに、名前でアドレス指定するのではなく、メモリ位置の値を直接変更することができます。ガーベジコレクタが物事を動かしてしまうため、メモリの破損を防ぐためにfixedキーワードを使用する必要があることに注意してください(そうしないと、 エラーCS0212が発生する可能性があります )。 "固定"された変数には書き込むことができないので、最初のポインタと同じ位置を指し示す最初のポインタを持つ必要があることがよくあります。
void Main()
{
int[] intArray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
UnsafeSquareArray(intArray);
foreach(int i in intArray)
Console.WriteLine(i);
}
unsafe static void UnsafeSquareArray(int[] pArr)
{
int len = pArr.Length;
//in C or C++, we could say
// int* a = &(pArr[0])
// however, C# requires you to "fix" the variable first
fixed(int* fixedPointer = &(pArr[0]))
{
//Declare a new int pointer because "fixedPointer" cannot be written to.
// "p" points to the same address space, but we can modify it
int* p = fixedPointer;
for (int i = 0; i < len; i++)
{
*p *= *p; //square the value, just like we did in SquarePtrParam, above
p++; //move the pointer to the next memory space.
// NOTE that the pointer will move 4 bytes since "p" is an
// int pointer and an int takes 4 bytes
//the above 2 lines could be written as one, like this:
// "*p *= *p++;"
}
}
}
出力:
1
4
9
16
25
36
49
64
81
100
unsafe
は、Cランタイムライブラリの_allocaのようなスタックにメモリを割り当てるstackallocの使用も許可します。上記の例を変更して、 stackalloc
を次のように使用することができます:
unsafe void Main()
{
const int len=10;
int* seedArray = stackalloc int[len];
//We can no longer use the initializer "{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}" as before.
// We have at least 2 options to populate the array. The end result of either
// option will be the same (doing both will also be the same here).
//FIRST OPTION:
int* p = seedArray; // we don't want to lose where the array starts, so we
// create a shadow copy of the pointer
for(int i=1; i<=len; i++)
*p++ = i;
//end of first option
//SECOND OPTION:
for(int i=0; i<len; i++)
seedArray[i] = i+1;
//end of second option
UnsafeSquareArray(seedArray, len);
for(int i=0; i< len; i++)
Console.WriteLine(seedArray[i]);
}
//Now that we are dealing directly in pointers, we don't need to mess around with
// "fixed", which dramatically simplifies the code
unsafe static void UnsafeSquareArray(int* p, int len)
{
for (int i = 0; i < len; i++)
*p *= *p++;
}
(出力は上記と同じです)
暗黙
implicit
キーワードは、変換演算子のオーバーロードに使用されます。たとえば、必要に応じて自動的にdouble
に変換されるFraction
クラスを宣言し、 int
から自動的に変換することができます:
class Fraction(int numerator, int denominator)
{
public int Numerator { get; } = numerator;
public int Denominator { get; } = denominator;
// ...
public static implicit operator double(Fraction f)
{
return f.Numerator / (double) f.Denominator;
}
public static implicit operator Fraction(int i)
{
return new Fraction(i, 1);
}
}
真偽
true
とfalse
キーワードには2つの用途があります。
- リテラルブール値
var myTrueBool = true;
var myFalseBool = false;
- 過負荷になる可能性のある演算子
public static bool operator true(MyClass x)
{
return x.value >= 0;
}
public static bool operator false(MyClass x)
{
return x.value < 0;
}
False演算子のオーバーロードは、 Nullable
型が導入される前のC#2.0以前では便利でした。
true
演算子に過負荷をかける型は、 false
演算子にも負担をかける必要があります。
文字列
string
は、テキスト(一連の文字)を格納できる.NETデータ型System.String
エイリアスです。
表記法:
string a = "Hello";
var b = "world";
var f = new string(new []{ 'h', 'i', '!' }); // hi!
文字列の各文字はUTF-16でエンコードされます。つまり、各文字に最低2バイトの記憶領域が必要です。
ushort
16ビットの正の整数を格納するために使用される数値型。 ushort
はSystem.UInt16
エイリアスで、2バイトのメモリをushort
ます。
有効な範囲は0
に65535
。
ushort a = 50; // 50
ushort b = 65536; // Error, cannot be converted
ushort c = unchecked((ushort)65536); // Overflows (wraps around to 0)
sbyte
8ビット符号付き整数を格納するために使用される数値型。 sbyte
はSystem.SByte
エイリアスで、1バイトのメモリをsbyte
しSystem.SByte
。符号なしの同等の場合は、 byte
使用しbyte
。
有効範囲は-127
に127
(残りの符号を格納するために使用されます)。
sbyte a = 127; // 127
sbyte b = -127; // -127
sbyte c = 200; // Error, cannot be converted
sbyte d = unchecked((sbyte)129); // -127 (overflows)
var
ユーザーが型を宣言したかのように強く型付けされた暗黙的に型指定されたローカル変数。他の変数宣言とは異なり、コンパイラは、この変数が割り当てられている値に基づいてこれが表す変数の型を決定します。
var i = 10; // implicitly typed, the compiler must determine what type of variable this is
int i = 10; // explicitly typed, the type of variable is explicitly stated to the compiler
// Note that these both represent the same type of variable (int) with the same value (10).
他のタイプの変数とは異なり、このキーワードを持つ変数定義は宣言されたときに初期化する必要があります。これは、暗黙的に型指定された変数を表すvarキーワードが原因です。
var i;
i = 10;
// This code will not run as it is not initialized upon declaration.
varキーワードを使用して、新しいデータ型を即座に作成することもできます。これらの新しいデータ型は匿名型として知られています 。これらのメソッドは、ユーザーが任意の種類のオブジェクト型を明示的に宣言する必要なく、一連のプロパティを定義できるため、非常に便利です。
平凡な匿名タイプ
var a = new { number = 1, text = "hi" };
匿名型を返すLINQクエリ
public class Dog
{
public string Name { get; set; }
public int Age { get; set; }
}
public class DogWithBreed
{
public Dog Dog { get; set; }
public string BreedName { get; set; }
}
public void GetDogsWithBreedNames()
{
var db = new DogDataContext(ConnectString);
var result = from d in db.Dogs
join b in db.Breeds on d.BreedId equals b.BreedId
select new
{
DogName = d.Name,
BreedName = b.BreedName
};
DoStuff(result);
}
foreach文ではvarキーワードを使用できます
public bool hasItemInList(List<String> list, string stringToSearch)
{
foreach(var item in list)
{
if( ( (string)item ).equals(stringToSearch) )
return true;
}
return false;
}
デリゲート
デリゲートは、メソッドへの参照を表す型です。メソッドを引数として他のメソッドに渡すために使用されます。
代理人は、静的メソッド、インスタンスメソッド、匿名メソッド、またはラムダ式を保持できます。
class DelegateExample
{
public void Run()
{
//using class method
InvokeDelegate( WriteToConsole );
//using anonymous method
DelegateInvoker di = delegate ( string input )
{
Console.WriteLine( string.Format( "di: {0} ", input ) );
return true;
};
InvokeDelegate( di );
//using lambda expression
InvokeDelegate( input => false );
}
public delegate bool DelegateInvoker( string input );
public void InvokeDelegate(DelegateInvoker func)
{
var ret = func( "hello world" );
Console.WriteLine( string.Format( " > delegate returned {0}", ret ) );
}
public bool WriteToConsole( string input )
{
Console.WriteLine( string.Format( "WriteToConsole: '{0}'", input ) );
return true;
}
}
デリゲートにメソッドを割り当てる場合、メソッドはパラメータと同じ戻り値の型を持たなければならないことに注意することが重要です。これは、メソッドのシグネチャを定義するパラメータだけが「通常の」メソッドのオーバーロードとは異なります。
イベントはデリゲートの上に構築されます。
イベント
event
によって、開発者は通知パターンを実装できます。
簡単な例
public class Server
{
// defines the event
public event EventHandler DataChangeEvent;
void RaiseEvent()
{
var ev = DataChangeEvent;
if(ev != null)
{
ev(this, EventArgs.Empty);
}
}
}
public class Client
{
public void Client(Server server)
{
// client subscribes to the server's DataChangeEvent
server.DataChangeEvent += server_DataChanged;
}
private void server_DataChanged(object sender, EventArgs args)
{
// notified when the server raises the DataChangeEvent
}
}
部分的な
キーワードpartial
は、クラス、構造体、またはインタフェースの型定義中に使用して、型定義を複数のファイルに分割することができます。これは自動生成されたコードに新しい機能を組み込むのに便利です。
File1.cs
namespace A
{
public partial class Test
{
public string Var1 {get;set;}
}
}
File2.cs
namespace A
{
public partial class Test
{
public string Var2 {get;set;}
}
}
注:クラスは任意の数のファイルに分割できます。ただし、すべての宣言は同じ名前空間と同じアセンブリの下になければなりません。
メソッドはpartial
キーワードを使用してpartial
と宣言することもできます。この場合、1つのファイルにはメソッド定義のみが含まれ、別のファイルには実装が含まれます。
部分的なメソッドは、部分型のある部分で定義されたシグネチャと、その型の別の部分で定義されたその実装を持っています。部分的なメソッドにより、クラスデザイナーは、イベントハンドラーと同様に、開発者が実装するかどうかを決めるメソッドフックを提供することができます。開発者が実装を提供しない場合、コンパイラはコンパイル時に署名を削除します。部分的な方法には、次の条件が適用されます。
- 部分型の両方の部分の署名は一致する必要があります。
- このメソッドはvoidを返す必要があります。
- アクセス修飾子は使用できません。部分的なメソッドは暗黙的にプライベートです。
- MSDN
File1.cs
namespace A
{
public partial class Test
{
public string Var1 {get;set;}
public partial Method1(string str);
}
}
File2.cs
namespace A
{
public partial class Test
{
public string Var2 {get;set;}
public partial Method1(string str)
{
Console.WriteLine(str);
}
}
}
注:部分メソッドを含む型も部分宣言する必要があります。