수색…


소개

키워드 는 컴파일러에게 특별한 의미가있는 사전 정의 된 예약 식별자입니다. @ 접두사없이 프로그램에서 식별자로 사용할 수 없습니다. 예를 들어 @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 의 현재 값이 항상 검색됩니다.

volatileclass 또는 struct 내의 필드에서만 사용할 수 있습니다. 다음 은 유효 하지 않습니다 .

public void MyMethod()
{
    volatile int x;
}

volatile 은 다음 유형의 필드에만 적용 할 수 있습니다.

  • 참조 유형 또는 알려진 유형으로 알려진 일반 유형 매개 변수
  • sbyte , byte , short , ushort , int , uint , char , floatbool 과 같은 기본 유형
  • byte , sbyte , short , ushort , int 또는 uint 기준으로 유형을 열거합니다.
  • IntPtrUIntPtr

비고 :

  • volatile 수정자는 대개 lock 문을 사용하지 않고 여러 스레드가 액세스하는 필드에 액세스를 직렬화하는 데 사용됩니다.
  • volatile 키워드는 참조 유형의 필드에 적용될 수 있습니다.
  • volatile 키워드는 32 비트 플랫폼 원자에서 64 비트 기본 요소에서 작동하지 않습니다. 같은 연동 작업 Interlocked.ReadInterlocked.Exchange 여전히 이러한 플랫폼에서 안전한 멀티 스레드 액세스를 위해 사용되어야합니다.

결정된

fixed 문은 한 위치에서 메모리를 수정합니다. 메모리에있는 객체는 일반적으로 움직이는 arround이며, 이로 인해 가비지 수집이 가능합니다. 그러나 메모리 주소에 안전하지 않은 포인터를 사용하면 해당 메모리를 이동해서는 안됩니다.

  • 우리는 가비지 수집기가 문자열 데이터를 재배치하지 않도록 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 반환합니다.

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 키워드와 다릅니다. const 필드는 필드 선언시에만 초기화 될 수 있습니다. 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
    }
}

참고 : 필드 읽기 전용을 선언하는 불변성을 의미하지는 않습니다. 필드가 참조 유형 이면 객체의 내용 을 변경할 수 있습니다. 읽기 전용 은 일반적으로 개체를 덮어 쓰지 않고 해당 개체의 인스턴스화 중에 만 할당하는 것을 방지하는 데 사용됩니다.

참고 : 생성자 내부에서 읽기 전용 필드를 다시 할당 할 수 있습니다.

public class Car
{
    public double Speed {get; set;}
}

//In code

private readonly Car car = new Car();

private void SomeMethod()
{
    car.Speed = 100;
}

같이

as 키워드는 캐스트 와 유사한 연산자입니다. 캐스트를 사용할 수없는 경우, as 를 사용하면 (자), InvalidCastException 아닌 null 가 생성됩니다.

expression as type expression is type ? (type)expression : (type)null 과 동등하다 expression is type ? (type)expression : (type)null 주의해야 할 점이하여 해당 as 참조 변환, 널 (NULL) 변환, 권투 변환에서만 유효합니다. 사용자 정의 변환은 지원 되지 않습니다 . 대신에 일반 캐스트를 사용해야합니다.

위의 확장을 위해 컴파일러는 expression 이 한 번만 평가되고 단일 동적 유형 검사 (위의 샘플에서 두 개와 다름)를 사용하는 코드를 생성합니다.

as 여러 종류를 용이하게하기 위해 인수를 예상 할 때 유용 할 수 있습니다. 구체적으로는 사용자가 여러 옵션을 부여 - 대신에 모든 가능성을 확인하는 것보다 is 주조, 아니면 그냥 캐스팅하고 예외를 잡기 전에. 하나의 언 박싱 페널티 만 발생시키는 객체를 주조 / 검사 할 때 'as'를 사용하는 것이 가장 좋습니다. 사용 is 확인 후 캐스팅이 언 박싱 처벌의 원인이됩니다.

인수가 특정 유형의 인스턴스가 될 것으로 예상되는 경우, 독자에게 더 명확한 목적으로 일반 형변환이 선호됩니다.

as 호출하면 null 이 생성 될 수 있으므로 항상 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 을 갖는 것과 본질적으로 같습니다.

결과적으로 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; , 어셈블리 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

try , catch , finallythrow 사용하면 코드에서 예외를 처리 할 수 ​​있습니다.

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) 결과가 반환되기 전에 실행됩니다.

잇다

즉시 둘러싸는 루프 구조의 다음 반복으로 제어권을 넘깁니다 (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);
}

산출:

에이

기음

.NET Fiddle에서의 라이브 데모

심판

refout 키워드는 값이 아닌 참조로 인수를 전달합니다. 값 유형의 경우, 이는 변수의 값이 수신자에 의해 변경 될 수 있음을 의미합니다.

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

사이의 주요 차이점 outref 키워드는 점이다 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!");
}

키워드를 일반 수정 자로 사용

out 키워드는 제네릭 인터페이스 및 대리자를 정의 할 때 제네릭 형식 매개 변수에서도 사용할 수 있습니다. 이 경우, out 키워드는 유형 매개 변수가 공 변 (covariant)임을 지정합니다.

공분산을 사용하면 generic 매개 변수로 지정된 유형보다 더 파생 된 유형을 사용할 수 있습니다. 이는 변형 인터페이스를 구현하는 클래스의 암시 적 변환과 대리자 유형의 암시 적 변환을 허용합니다. 공분산 및 반항은 참조 유형에 지원되지만 값 유형에는 지원되지 않습니다. - 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

확인 됨, 선택 취소됨

checkedunchecked 키워드는 연산이 수학 오버 플로우를 처리하는 방법을 정의합니다. checkedunchecked 키워드의 컨텍스트에서 "오버플로"는 정수 산술 연산의 결과가 대상 데이터 유형이 나타낼 수있는 것보다 큰 값을 갖는 경우입니다.

checked 블록 내에서 오버 플로우가 발생할 때 (또는 컴파일러가 체크 된 산술을 전역 적으로 사용하도록 설정 한 경우) 원치 않는 동작에 대해 경고하기 위해 예외가 throw됩니다. 한편 unchecked 블록에서는 오버플로가 발생하지 않으며 예외가 발생하지 않으며 값이 반대쪽 경계로 간단하게 줄 바꿈됩니다. 이것은 미묘하고 찾기 힘든 버그로 이어질 수 있습니다.

대부분의 산술 연산은 오버플로 할만큼 크거나 작지 않은 값에 대해 수행되기 때문에 대부분의 경우 블록을 명시 적으로 정의 된대로 checked 할 필요가 없습니다. 재귀 함수에서 산술을 수행하거나 사용자 입력을받는 경우와 같이 오버플로를 초래할 수있는 제한되지 않은 입력에 대해 산술을 수행 할 때주의를 기울여야합니다.

checked 또는 checked 하지 unchecked 부동 소수점 산술 연산에 영향을 미치지 않습니다.

블록 또는 표현식이 unchecked 것으로 선언되면 그 내부의 산술 연산이 오류없이 오버플로 될 수 있습니다. 이 동작이 필요한 예제는 값이 계산 중에 "랩 어라운드 (wrap around)"할 수있는 체크섬 계산입니다.

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 가장 일반적인 용도 중 하나는 체크섬 유형 인 object.GetHashCode() 대한 사용자 정의 재정의를 구현하는 것입니다. 이 질문에 대한 대답에서 키워드의 용도를 볼 수 있습니다 : 우선 적용되는 System.Object.GetHashCode에 대한 최상의 알고리즘은 무엇입니까? .

블록 또는 표현식이 checked 로 선언되면 오버플로를 발생시키는 모든 산술 연산으로 인해 OverflowException 이 throw됩니다.

int SafeSum(int x, int y) {
    checked { // checked block
        return x + y; 
    }
}

checked와 unchecked는 모두 블록 및 표현 형식 일 수 있습니다.

체크 된 블록과 체크되지 않은 블록은 호출 된 메서드에 영향을 미치지 않고 현재 메서드에서 직접 호출 된 연산자 만 영향을받습니다. 예를 들어 Enum.ToObject() , Convert.ToInt32() 및 사용자 정의 연산자는 사용자 정의 체크 / 체크되지 않은 컨텍스트의 영향을받지 않습니다.

참고 : 기본 오버플로 기본 동작 (확인 대 체크되지 않음)은 프로젝트 속성 또는 / checked [+ | -] 명령 줄 스위치를 통해 변경 될 수 있습니다. 디버그 빌드의 경우 확인 된 작업을 기본값으로 지정하고 릴리스 빌드의 경우 선택하지 않는 것이 일반적입니다. checked 키워드와 unchecked 키워드는 기본 접근 방식이 적용되지 않는 경우에만 사용되며 정확성을 보장하려면 명시 적 동작이 필요합니다.

고토

goto 는 레이블로 지정된 코드 안의 특정 행으로 점프하는 데 사용될 수 있습니다.

goto A와 :

상표:

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 #이 fall-through case 블록을 지원하지 않기 때문에 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 case.

  • 다중 레벨 나누기. 대신 LINQ를 대신 사용할 수는 있지만 일반적으로 성능이 떨어집니다.

  • 언 랩된 저수준 객체로 작업 할 때 자원 할당 해제. C #에서는 하위 수준의 개체를 일반적으로 별도의 클래스로 래핑해야합니다.

  • 유한 상태 기계 (예 : 파서). 컴파일러에서 생성 된 비동기 / 대기 상태 머신을 통해 내부적으로 사용됩니다.

열거 형

enum 키워드는 프로그래머가 명시 적으로 상속하지 않고이 클래스가 추상 클래스 Enum 에서 상속 받았다는 것을 컴파일러에 알립니다. EnumValueType 의 자손이며 고유 한 명명 된 상수 세트와 함께 사용하기위한 것입니다.

public enum DaysOfWeek
{
    Monday,
    Tuesday,
}

선택적으로 각 값 (또는 그 중 일부)에 대해 특정 값을 지정할 수 있습니다.

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

이 예제에서는 0 값을 생략했습니다. 이는 일반적으로 나쁜 습관입니다. enum 은 항상 명시 적 변환 (YourEnumType) 0 의해 생성 된 기본값 (YourEnumType) 0 . YourEnumType 은 선언 된 enume 유형입니다. 값 0을 정의하지 않으면 초기화시 enum 에 정의 된 값이 없습니다.

enum 기본 유형은 int 이며 기본 유형을 byte , sbyte , short , ushort , int , uint , longulong 포함한 모든 정수 유형으로 변경할 수 있습니다. 아래는 기본 형식의 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 는 배열 요소 또는 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 는 콜렉션이 bool MoveNext() 메서드를 노출하는 객체를 반환해야하는 object GetEnumerator() bool MoveNext() 메서드와 object Current { get; } 속성.

매개 변수

params 는 메소드 매개 변수가 가변 개수의 인수를 수신하도록 허용합니다. 즉, 0, 하나 또는 여러 인수가 해당 매개 변수에 허용됩니다.

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 오버로드를 사용하기 전에보다 구체적인 오버로드를 선호합니다. 예를 들어 두 가지 방법이있는 경우 :

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 문은 가장 안쪽 루프의 실행을 중단하고 그 뒤의 코드로 돌아갑니다. 또한 iterator가 끝났음을 지정하는 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; 
    }
}

여기 외부 루프에서 벗어나고 싶다면 다음과 같은 몇 가지 전략 중 하나를 사용할 수 있습니다.

  • 루프 구조 전체에서 뛰어 내리기위한 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 는 .NET 데이터 유형 인 System.Single 대한 별명입니다. IEEE 754 단 정밀도 부동 소수점 숫자를 저장할 수 있습니다. 이 데이터 형식은 모든 C # 프로젝트를 만들 때 암시 적으로 참조되는 mscorlib.dll 있습니다.

대략적인 범위 : -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.dllmscorlib.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 키워드는 클래스 (객체)의 현재 인스턴스를 참조합니다. 이렇게하면 클래스 이름 (필드)과 메소드의 매개 변수 (또는 로컬 변수)에서 같은 이름을 가진 두 변수를 구별 할 수 있습니다.

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 지므로 this.MemberOfTypeMemberOfType 은 동등합니다. base 키워드도 참조하십시오.

확장 메서드는 현재 인스턴스에서 호출하는 경우, 참고 this 필요합니다. 예를 들어, IEnumerable<> 을 구현하는 클래스의 비 정적 메서드 안에 있고 이전에 Count 함수를 호출하려면 다음을 사용해야합니다.

this.Count()  // works like StaticClassForExtensionMethod.Count(this)

this 거기에서 생략 될 수 없다.

...에 대한

구문 : for (initializer; condition; iterator)

  • 반복 횟수가 알려진 경우 for 루프가 자주 사용됩니다.
  • 루프에 들어가기 전에 initializer 섹션의 명령문은 한 번만 실행됩니다.
  • condition 섹션에는 모든 루프 반복 끝에 루프가 종료되어야하는지 아니면 다시 실행되어야 하는지를 결정하기 위해 평가되는 부울식이 포함되어 있습니다.
  • iterator 섹션은 루프 본문을 반복 할 때마다 어떤 일이 일어나는지 정의합니다.

다음 예제는 to를 사용하여 문자열의 문자를 반복하는 방법 for 보여줍니다.

string str = "Hello";
for (int i = 0; i < str.Length; i++)
{
    Console.WriteLine(str[i]);                
}

산출:

H
이자형


영형

.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 연산자는 조건부 쿼리가 거짓이거나 코드가 goto , return , break 또는 throw 문으로 인터럽트 될 때까지 코드 블록을 반복합니다.

while 키워드 구문 :

동안 ( 조건 ) { 코드 블록; }

예:

int i = 0;
while (i++ < 5)
{
    Console.WriteLine("While is on loop number {0}.", i);
}

산출:

"동안 루프 번호 1입니다."
"while 회선 번호는 2입니다."
"동안 루프 번호 3입니다."
"동안 루프 번호 4입니다."
"동안 루프 번호 5입니다."

.NET Fiddle에서의 라이브 데모

while 루프는 Entry Controlled 이며, 닫힌 코드 블록을 실행 하기 전에 조건을 검사하면됩니다. 이것은 while 루프가 조건이 거짓 일 때 명령문을 실행하지 않는다는 것을 의미합니다.

bool a = false;

while (a == true)
{
    Console.WriteLine("This will never be printed.");
}

어떤 조건에서 프로비저닝하지 않고 while 조건을 false로 지정하면 무한 루프 또는 무한 루프가 발생합니다. 가능하면 피해야하지만 예외적 인 경우가있을 수 있습니다.

다음과 같이 이러한 루프를 만들 수 있습니다.

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 키워드는 세 가지 용도로 사용됩니다.

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

~을 사용하여

using statementusing directive using statement using 키워드 사용을 using 하는 두 가지 유형이 있습니다.

  1. using 문 사용 :

    using 키워드는 IDisposable 인터페이스를 구현하는 객체가 사용 후 적절히 처리되도록합니다. using 문에 대한 별도의 항목이 있습니다 .

  2. using 지시어

    using 지시문은 세 가지 용도로 using 지시문의 msdn 페이지를 참조하십시오. using 지시문 에는 별도의 항목이 있습니다.

봉인 된

sealed modifier는 클래스에 적용될 때 다른 클래스가 클래스에서 상속하는 것을 방지합니다.

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 은 클래스, 필드, 메서드, 속성, 연산자, 이벤트 및 생성자와 함께 사용할 수 있습니다.

클래스의 인스턴스에는 클래스의 모든 인스턴스 필드의 별도 사본이 포함되어 있지만 각 정적 필드의 복사본은 하나뿐입니다.

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

countA 클래스의 인스턴스의 총 수와 같습니다.

정적 수정자는 클래스에 대한 정적 생성자를 선언하거나 정적 데이터를 초기화하거나 한 번만 호출하면되는 코드를 실행하는 데에도 사용할 수 있습니다. 정적 생성자는 클래스가 처음 참조되기 전에 호출됩니다.

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 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 의 별칭입니다. 이 데이터 형식은 mscorlib.dll 에서 찾을 수 있습니다. mscorlib.dll 은 모든 C # 프로젝트를 만들 때 암시 적으로 참조됩니다.

범위 : -2,147,483,648 ~ 2,147,483,647

int int1 = -10007;
var int2 = 2132012521;     

long 키워드는 부호가있는 64 비트 정수를 나타내는 데 사용됩니다. mscorlib.dll 에있는 System.Int64 데이터 형식의 별칭으로, C # 프로젝트를 만들 때 암시 적으로 참조됩니다.

long 변수는 명시 적으로나 묵시적으로 선언 할 수 있습니다.

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

다음 예제에서는 Newtonsoft의 Json.NET 라이브러리에서 dynamic 을 사용하여 비 직렬화 JSON 파일에서 데이터를 쉽게 읽을 수 있습니다.

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
}

동적 키워드와 관련된 몇 가지 제한 사항이 있습니다. 그 중 하나는 확장 방법을 사용하는 것입니다. 다음 예제에서는 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 합니다. 이 문제를 해결하려면 static 클래스를 통해 확장 메서드를 호출해야합니다.

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

B.Foo() 가 자동으로 A.Foo() 재정의하지 않기 때문에 위의 예에서는 경고 CS0108 도 발생합니다. 추가 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 (Task Parallel Library)을 활용합니다. asyncawait 키워드는 아래 표시된 것과 동일한 함수에서 쌍으로 사용됩니다. await 키워드는 await 중인 비동기 작업이 완료되거나 결과가 반환 될 때까지 현재의 비동기 메서드 실행을 일시 중지하는 데 사용됩니다. await 키워드를 사용하려면 키워드를 사용하는 메소드에 async 키워드를 표시해야합니다.

void 와 함께 async 를 사용하는 것은 권장하지 않습니다. 더 많은 정보를 원하시면 여기를 클릭하십시오 .

예:

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> 반환 메서드가 단일 비동기 작업 만 반환하는 경우 키워드 쌍 asyncawait 를 생략 할 수 있습니다.

이보다 :

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 await 사용할 수 없습니다 catchfinally .

6.0

C # 6.0 await 사용할 수 있습니다 catch 하고 finally .

char는 변수 안에 저장된 단일 문자입니다. 이것은 2 바이트의 메모리 공간을 차지하는 내장 된 값 유형입니다. mscorlib.dll 있는 System.Char 데이터 형식을 System.Char 파일 형식은 모든 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 은 코드 블록에 대한 스레드 안전을 제공하므로 동일한 프로세스 내에서 하나의 스레드 만 액세스 할 수 있습니다. 예:

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 와 함 2 lock 키워드를 사용할 수 있습니다.

_objLocknull 될 수 없으며 코드를 실행하는 여러 스레드는 동일한 객체 인스턴스를 사용해야합니다 ( static 필드로 만들거나 두 스레드에 동일한 클래스 인스턴스 사용)

컴파일러 측면에서 lock 키워드는 Monitor.Enter(_lockObj); 로 대체되는 구문 설탕입니다 Monitor.Enter(_lockObj);Monitor.Exit(_lockObj); . 따라서 이러한 두 가지 방법으로 코드 블록을 둘러 쌈으로써 자물쇠를 교체하면 동일한 결과를 얻을 수 있습니다. 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 ( 0 , 0f , 0m )
  • null 문자 ( '\0' )

때로는 무언가가 null이거나 빈 / 기본 객체인지 확인하는 것이 중요합니다. 이를 확인하기 위해 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 일반적인 인수에 제약 유형 및 필터링 LINQ 쿼리 : C #에서 두 가지 목적을 수행 할 수 있습니다.

일반적인 수업에서는

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, Byte, 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 키워드는 외부 적으로 구현 된 메서드를 선언하는 데 사용됩니다. Interop 서비스를 사용하여 관리되지 않는 코드를 호출하려면 DllImport 특성과 함께 사용할 수 있습니다. 이 경우 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 메서드를 사용합니다.

외부 어셈블리 별칭을 정의하는데도 사용할 수 있습니다. 이는 단일 어셈블리에서 동일한 구성 요소의 다른 버전을 참조하게합니다.

동일한 정규화 된 형식 이름을 가진 두 어셈블리를 참조하려면 다음과 같이 명령 프롬프트에서 별칭을 지정해야합니다.

/r:GridV1=grid.dll
/r:GridV2=grid20.dll

그러면 외부 별칭 GridV1 및 GridV2가 만들어집니다. 프로그램 내에서 이러한 별칭을 사용하려면 extern 키워드를 사용하여 별칭을 참조하십시오. 예 :

extern alias GridV1;
extern alias GridV2;

불량배

부울 값 truefalse 를 저장하기위한 키워드. bool은 System.Boolean의 별칭입니다.

bool의 기본값은 false입니다.

bool b; // default value is false
b = true; // true
b = ((5 + 2) == 6); // false

bool이 null 값을 허용하려면 bool로 초기화해야합니다.

bool의 기본값은 무엇입니까? null

bool? a // default value is null

언제

whenC # 6에 추가 된 키워드이며 예외 필터링에 사용됩니다.

when 키워드가 도입되기 전에 각 예외 유형에 대해 하나의 catch 절이있을 수있었습니다. 키워드를 추가하면보다 세분화 된 제어가 가능합니다.

when 표현이 부착되어 catch 지점, 그리고 경우에만 when 상태가 truecatch 절은 실행됩니다. 같은 예외 클래스 유형을 사용하는 여러 catch 문을 가질 수 있으며 조건이 다른 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 키워드가 없으면 두 추가 작업 중 어느 것도 컴파일되지 않습니다.

언제 이것이 유용한가?

이는 오버플로 검사에 시간이 걸리거나 오버플로 / 언더 플로가 원하는 동작 (예 : 해시 코드 생성시)이 될 때 오버플로가 발생하지 않는 계산 속도를 높일 수 있으므로 유용합니다.

예약어 "void"System.Void 유형의 별칭이며 다음 두 가지 용도로 사용됩니다.

  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 일 수도 있습니다.

void 포인터 유형을 선언하는 것은 Microsoft에 의해 권장되지 않습니다.

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 는 조건이 false로 평가 될 경우 실행될 else 절도 가질 수 있습니다.

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"

당신이 경우에 사용하는 경우 조건이 위의 예에 충족되는 경우, 컨트롤이 다른 테스트를 건너 뛰고 다른 construct.So 경우 특정의 끝으로 이동하는 것이 중요, 시험의 순서는 .. 다른 구조의 경우 중요

C # 부울 식은 단락 회로 평가를 사용 합니다. 평가 조건이 부작용을 일으킬 수있는 경우 중요합니다.

if (someBooleanMethodWithSideEffects() && someOtherBooleanMethodWithSideEffects()) {
  //...
}

someOtherBooleanMethodWithSideEffects 가 실제로 실행된다는 보장은 없습니다.

초기 조건이 나중에 평가할 때 "안전"하다는 것을 보장하는 경우에도 중요합니다. 예 :

if (someCollection != null && someCollection.Count > 0) {
   // ..
}

주문을 거꾸로 처리하면 주문이 매우 중요합니다.

if (someCollection.Count > 0 && someCollection != null) {

someCollectionnull 경우, NullReferenceException 가 슬로우됩니다.

해야 할 것

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

산출:

"루프 번호 1에 있습니다."
"Do가 루프 번호 2에 있습니다."
"루프 번호 3에 있습니다."
"루프 번호 4에 있습니다."
"루프 번호 5에 있습니다."

while 루프와 달리 do-while 루프는 Exit Controlled 입니다. 즉, do-while 루프는 조건이 처음 실패하더라도 적어도 한 번은 명령문을 실행합니다.

bool a = false;

do
{
    Console.WriteLine("This will be printed once, even if a is false.");
} while (a == true);

운영자

대부분의 기본 제공 연산자 (변환 연산자 포함)는 publicstatic 한정자와 함께 operator 키워드를 사용하여 오버로드 할 수 있습니다.

연산자는 단항 연산자, 2 진 연산자 및 변환 연산자의 세 가지 형식으로 제공됩니다.

단항 및 이항 연산자에는 포함 유형과 동일한 유형의 매개 변수가 적어도 하나 필요하고 일부에는 상보 일치 연산자가 필요합니다.

변환 연산자는 둘러싸는 유형으로 변환하거나 변환해야합니다.

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 문은 하나 이상의 스위치 섹션을 포함합니다. 각 스위치 섹션에는 하나 이상의 case 레이블 뒤에 하나 이상의 명령문이옵니다. 대소 문자 레이블에 일치하는 값이 없으면 default 섹션 (있는 경우) default 전송됩니다. C #에서는 엄밀히 말하면 사례 폴스 - 스루가 지원되지 않습니다. 그러나 하나 이상의 case 레이블이 비어 있으면 실행은 코드가 포함 된 다음 case 블록의 코드를 따릅니다. 동일한 구현으로 여러 case 레이블을 그룹화 할 수 있습니다. 다음 예제에서 month 12 일 case 레이블 12 12 가 그룹화되어 있으므로 case 2 의 코드가 실행됩니다. 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 값은 ( 리터럴 값).

인터페이스

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

포인터로 작업하는 동안 메모리 위치 값을 이름으로 지정하지 않고 직접 변경할 수 있습니다. 가비지 컬렉터가 물건을 움직일 때 메모리 손상을 막기 위해 고정 키워드를 사용해야하는 경우가 종종 있습니다 (그렇지 않으면 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

unsafestackalloc 을 사용하여 C 런타임 라이브러리의 _alloca와 같은 스택에 메모리를 할당합니다. 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 클래스를 선언 할 수 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 키워드는 두 가지 용도로 사용됩니다.

  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 바이트의 저장 공간이 필요합니다.

우승자

16 비트 양의 정수를 저장하는 데 사용되는 숫자 유형입니다. ushortSystem.UInt16 의 별칭이며 2 바이트의 메모리를 사용합니다.

유효한 범위는 0 ~ 65535 입니다.

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

스 비테

8 비트 부호있는 정수를 저장하는 데 사용되는 숫자 유형입니다. sbyteSystem.SByte 의 별칭이며 1 바이트의 메모리를 사용합니다. 부호없는 동등한 경우 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
    }
}

MSDN 참조

부분적인

키워드 partial 은 유형 정의가 여러 파일로 분리되도록 class, struct 또는 interface의 유형 정의 중에 사용할 수 있습니다. 이는 자동 생성 된 코드에 새로운 기능을 통합하는 데 유용합니다.

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 키워드를 사용하여 메소드를 부분적으로 선언 할 수도 있습니다. 이 경우 하나의 파일에는 메소드 정의 만 포함되고 다른 파일에는 구현이 포함됩니다.

부분 메소드는 부분 유형의 한 부분에 정의 된 서명과 유형의 다른 부분에 정의 된 구현을 갖습니다. 부분 메소드를 사용하면 클래스 디자이너가 개발자가 구현할지 여부를 결정할 수있는 이벤트 핸들러와 유사한 메소드 훅을 제공 할 수 있습니다. 개발자가 구현을 제공하지 않으면 컴파일러는 컴파일 타임에 서명을 제거합니다. 다음 조건이 부분 메소드에 적용됩니다.

  • 부분 유형의 두 부분에서 서명이 일치해야합니다.
  • 메서드는 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