サーチ…


前書き

キーワードは、あらかじめ定義された予約済み識別子で、コンパイラにとって特別な意味を持ちます。 @接頭辞を付けずにプログラムの識別子として使うことはできません。例えば@ifキーワード有効な識別子ではありませんif

備考

C#には、それぞれが特別な機能を持つ定義済みの "キーワード"(または予約語)のコレクションがあります。これらの単語は、 @前に付けない限り、識別子(変数、メソッド、クラスなどの名前)として使用することはできません。

これらとは別に、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 = 5y = x + 10の値と判断しyいつものようにこのように、それが最後のステートメントを最適化する15ようにしてしまうy = 15 。しかし、変数xは実際にはpublicフィールドであり、 xの値はこのフィールドで別々に動作する別のスレッドを通じて実行時に変更される可能性があります。この変更されたコードブロックを考えてみましょう。フィールドxvolatileとして宣言されていることに注意してください。

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()
{
    volatile int x;
}

volatileは次の型のフィールドにしか適用できません:

  • 参照型または既知の型として知られているジェネリック型パラメータ
  • sbytebyteshortushortintuintcharfloatboolなどのプリミティブ型
  • bytesbyteshortushortintまたはuint基づいて型をsbyte
  • IntPtrUIntPtr

備考:

  • volatile修飾子は通常、lock文を使用してアクセスをシリアライズせずに複数のスレッドがアクセスするフィールドに使用されます。
  • volatileキーワードは、参照型のフィールドに適用できます。
  • volatileキーワードは、32ビットプラットフォーム上の64ビットプリミティブでは動作しません。以下のような連動操作Interlocked.ReadInterlocked.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];
}

fixedstructフィールドでのみ使用できます(安全でないコンテキストでも使用する必要があります)。

デフォルト

クラス、インタフェース、デリゲート、配列、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キーワードとは異なりreadonlyconstフィールドは、フィールドの宣言でのみ初期化できます。 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 typeexpression 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);

.NET Fiddleのライブデモ

次のように使用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の同じコード部分:

7.0
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宣言ではすべての型を使用できるわけではありません。許容される値の型は、 sbytebyteshortushortintuintlongulongcharfloatdoubledecimalbool 、およびすべてのenum型です。他の値型( TimeSpanGuid )で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
        }
    }
}

試してみて

trycatchfinallythrow使用すると、コード内の例外を処理できます。

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

.NET Fiddleのライブデモ

var stuff = new [] {"a", "b", null, "c", "d"};

foreach (var s in stuff)
{
    if (s == null)
    {
        continue;
    }           
    Console.WriteLine(s);
}

出力:

a
b
c
d

.NET Fiddleのライブデモ

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;
}

.NET Fiddleのライブデモ

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れたものとして明示的に定義する必要はありません。例えば、再帰関数で算術演算を行うときやユーザー入力を受けているときなど、オーバーフローを引き起こす可能性のある制限のない入力に対して算術演算を行うときは注意が必要です。

checkedunchecked 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;
}

.NET Fiddleのライブデモ

ケースステートメント:

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
}

.NET Fiddleのライブデモ

これは、C#がフォールスルーケースブロックをサポートしていないため、switch文で複数の動作を実行する場合に特に便利です。

例外の再試行

var exCount = 0;
retry:
try
{
    //Do work
}
catch (IOException)
{
    exCount++;
    if (exCount < 3)
    {
        Thread.Sleep(100);
        goto retry;
    }
    throw;
}

.NET Fiddleのライブデモ

多くの言語と同様に、以下の場合を除いて、gotoキーワードの使用はお勧めしません。

C#に適用されるgoto有効な使用法

  • switch文のfall-throughの場合

  • マルチレベルブレーク。代わりにLINQを使用することもできますが、通常はパフォーマンスが低下します。

  • アンラップされた低レベルのオブジェクトを扱うときのリソースの割り当て解除。 C#では、低レベルのオブジェクトは通常別のクラスにラップする必要があります。

  • 有限状態機械、例えばパーサー;コンパイラによって生成された非同期/待機状態マシンを内部的に使用します。

列挙型

enumキーワードは、明示的に継承することなく、このクラスが抽象クラスEnumから継承することをコンパイラに伝えます。 EnumValueType子孫であり、別の名前付き定数のセットで使用することを意図しています。

public enum DaysOfWeek
{
    Monday,
    Tuesday,
}

オプションで、それぞれの値(またはその一部)を指定することもできます。

public enum NotableYear
{
   EndOfWwI = 1918;
   EnfOfWwII = 1945,
}

この例では0の値を省略しましたが、これは通常悪い習慣です。 enumは明示的な変換(YourEnumType) 0によって生成されるデフォルト値を常に持ちます。ここでYourEnumTypeは宣言されたenume型です。 0の値が定義されていなければ、 enumは開始時に定義された値を持ちません。

enumのデフォルトの基になる型はintです。基礎となる型をbytesbyteshortushortintuintlongulongなどの整数型に変更できます。以下は、基礎となる型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);
}

これは出力されます

"こんにちは世界!"
"ご機嫌はいかがですか?"
"さようなら"

.NET Fiddleのライブデモ

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, 

.NET Fiddleのライブデモ

反復の順序は、配列や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.");
}

.NET Fiddleのライブデモ

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");
}

.NET Fiddleのライブデモ

他の言語とは異なり、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.MemberOfTypeMemberOfType 、その場合に相当します。 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

.NET Fiddleのライブデモ

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

.NET Fiddleのライブデモ

while

while演算子は、条件付きクエリがfalseに等しいか、またはコードがgotoreturnbreakまたは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です。"

.NET Fiddleのライブデモ

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

  1. usingステートメント

    usingキーワードは、 IDisposableインターフェイスを実装するIDisposableが使用後に適切に処理されることを保証します。 usingステートメントには別のトピックがあります

  2. 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 classstaticキーワードでマークされ、パラメータを処理する一連のメソッドの有益なコンテナとして使用できますが、必ずしもインスタンスに結び付ける必要はありません。クラスの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 staticusing staticせずに:

using System;

public class ConsoleApplication
{
    public static void Main()
    {
         Console.WriteLine("Hello World!"); //Writeline is method belonging to static class Console
    }

}

using staticusing 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キーワードを指定する必要があります。

asyncvoid併用はお勧めしません。詳細はこちらをご覧ください

例:

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);
}
5.0

C#5.0では、 catchfinallyではawaitを使用できません。

6.0

C#6.0では、 catchfinally使用がawaitます。

チャー

charは変数の中に格納された1文字です。これは、2バイトのメモリ空間を必要とするビルトインの値型です。これは、 mscorlib.dllあるSystem.Charデータ型を表していますmscorlib.dllは、作成時にすべてのC#プロジェクトによって暗黙的に参照されます。

これを行うには複数の方法があります。

  1. char c = 'c';
  2. char c = '\u0063'; //Unicode
  3. char c = '\x0063'; //Hex
  4. 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キーワードを使用できます。

_objLocknullすることはできません。また、コードを実行する複数のスレッドは、同じオブジェクトインスタンスを使用する必要があります( 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>()
  • 空の文字列( ""
  • 数字のゼロ( 00f0m
  • ヌル文字( '\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(これにはintbooleanなどの 'プリミティブ'データ型が含まれています)のみを指定できます

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

いつ

whenC#6で追加されたキーワードで、例外フィルタリングに使用されます。

whenキーワードを導入する前に、例外の種類ごとに1つのcatch節を持つことができました。キーワードの追加により、より細かい制御が可能になりました。

when式はに添付されたcatch支店、および場合にのみwhenの条件があるtruecatch句が実行されます。同じ例外クラスタイプを持つ複数の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つの用途があります。

  1. 戻り値を持たないメソッドを宣言します。
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.
}
  1. 安全でないコンテキストの不明な型へのポインタを宣言します。

安全でないコンテキストでは、型はポインタ型、値型、参照型のいずれかです。ポインタ型宣言は通常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"

ifelse節もあり、条件が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) {

someCollectionnull場合、 NullReferenceExceptionがスローされnull

行う

do演算子は、条件付きクエリがfalseに等しくなるまで、コードのブロックを反復処理します。 do-whileループは、 gotoreturnbreakまたは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のcasecaseラベル12 12がグループ化されているため、 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);
    }
}

真偽

truefalseキーワードには2つの用途があります。

  1. リテラルブール値
var myTrueBool = true;
var myFalseBool = false;
  1. 過負荷になる可能性のある演算子
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ビットの正の整数を格納するために使用される数値型。 ushortSystem.UInt16エイリアスで、2バイトのメモリをushortます。

有効な範囲は065535

ushort a = 50; // 50
ushort b = 65536; // Error, cannot be converted
ushort c = unchecked((ushort)65536); // Overflows (wraps around to 0)

sbyte

8ビット符号付き整数を格納するために使用される数値型。 sbyteSystem.SByteエイリアスで、1バイトのメモリをsbyteSystem.SByte 。符号なしの同等の場合は、 byte使用しbyte

有効範囲は-127127 (残りの符号を格納するために使用されます)。

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
    }
}

MSDNリファレンス

部分的な

キーワード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);
        }
    }
}

注:部分メソッドを含む型も部分宣言する必要があります。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow