본문 바로가기
c#

c# 7.0...8.0

by kcj3054 2022. 12. 6.

in..

  • 메서드의 매개변수에 in변경자 추가 -> in은 ref + readonly이다..

  • 값 복사 부하를 줄이기 위해서 ref로 변경한다면 오동할 수 있는 프로그램이 만들어 질 수 있다 -> 값 호출자쪽에서는 값변경을 원하지 않을 수도 있기때문이다 이를 위해서 ref + readonly인 in을 추가..

Vector v1 = new();
StructParam(in v1); // 불필요한 값 복사 없다.

void StructParam(in Vector v)
{
    // v.x = 5; in으로 인해서 변경 불가
}


struct Vector
{
    public int x { get; set; }
    public int y { get; set; }
}

readonly 구조체

  • 일반적으로 readonly는 값을 직접 변경하는 코드는 컴파일단계에서 오류가 발생한다. 하지만 메서드 호출에 대해서는 허용한다는 문제가..

이상한 행위..

readonly Vector v1 = new();
v1.Increment();

struct Vector
{
    public int x { get; set; }
    public int y { get; set; }

    public void Increment()
    {
        x++;
        y++;
    }
}

  • 위의 행위가 일어나는 것은 c#의 방어 복사본처리때문이다. c# 컴파일러는 readonly가 적용된 구조체 인스턴스에 한해 그 상태를 유지할 수 있도록한다...

  • 컴파일러가 처리하는 행위는 -> Vector tmp = v1; //원본 v1을 변경하지 않도록 방어 복사본 처리.. , tmp.Increment();

올바른 행위..



readonly Vector v1 = new();
readonly struct Vector
{
    readonly public int x;
    readonly public int y;
}

  • readonly struct 값에 대해서는 내부 맴버에 대해서도 모두 readonly를 붙일 것을 강제한다 그래서 위의 이상한 행위 예제는 될 수 없다..

ref struct

  • struct는 스택을 사용하지만 struct가 class안에 정의된 경우에는 힙에 데이터가 위치하게된다... 값 형식을 스택에만 생성할 수 있도록 강제할 수 있는 방법이 ref struct이다...
ref struct Matrix // : IDisposable  Interface를 구현할 수 없다..-> 왜냐하면 인스턴스로 형변환은 스택 객체가 힙객체로 반환돼야 하는 박싱 작업
{
    public Vector v1; //ref struct vector...
    public Vector v2;
}
  • 한가지 제약은 interface를 구현할 수없다.. 하지만 8.0인가 추후에 보면 ref struct안에서도 void Dispose를 구현한다면 using을 사용할 수 있도록해주었다..

제네릭 제약조건이 system.Delegate, system.Enum에 대해서는 풀린다...



{
    Action action = () => { Console.WriteLine("Action"); };

    ActionProxy<Action> proxy = new(action);
    proxy.Call();
}

{
    Action<string> action = (arg) => { Console.WriteLine($"{arg}"); };
    ActionProxy<Action<string>> proxy = new(action);
    proxy.Call();
}


class ActionProxy<T> where T : System.Delegate
{
    private T _callbackFunc;

    public ActionProxy(T callbackFunc)
    {
        _callbackFunc = callbackFunc;
    }

    public void Call()
    {
        switch (_callbackFunc)
        {
            case Action action:
                action();
                break;
            case Action<string> action:
                action("kcjkcjkcjckjckcjkcom2");
                break;
        }
    }
}

^ .. 연산자..


string text = "kcj3054";
System.Range full = 0..^0;
string copy = text[full];
  • 범위연산자.. ^n연산자는 뒤에서부터 시작된다 , 참고로 인덱스 연산자는 마지막 위치를 1로한다..

  • n1..n2 [) 범위랑 동일하다..

간결해진 using

  • 기존
using (var file = new System.IO.StreamReader("TSDFA.TXT"))
{
    //~~
}
  • 변하된 것

void Init()
{
    using var file2 = new StreamReader("TEST.TXT");

    string txt = file2.ReadToEnd();
    //~~
}
  • 기존의 using문을 보면 { 중괄호로 범위를 나타내주고 있다 해당 범위 안이면 Idspose같은 자동적으로 dispose해준다. 그렇지만 더 편리해진 using은.. 범위가 가장 가까운 중괄호로변하게 된다.. 편리하다.

부분 메서드..

  • 부분 메서드는 코드는 분할하지 않고.. 단지 메서드의 선언과 구현부를 분리할 수 있게 허용한다..



MyTest myTest = new();

myTest.WriteTest();

partial class MyTest
{
    partial void Log(object obj);

    public void WriteTest()
    {
        this.Log("call test!");
    }
}


//다른 파일

partial class MyTest
{
    partial void Log(Object obj)
    {
        Console.WriteLine(obj.ToString());
    }
}
  • 위에서 보듯이 Log메서드는 위쪽에는 선언부분만 있지만, 아랫부분에는 구현부까지 존재한다

컬렉션과 람다메서드

List<int> list = new List<int>() {1, 2, 3, 4, 5};

list.ForEach((elemn) => {Console.WriteLine(elemn);} );

Array.ForEach(list.ToArray(),
    (elem) => {Console.WriteLine(elem);});


    list.ForEach(delegate(int elem) {Console.WriteLine(elem);});
  • 위에서 보듯이 컬렉션 list에서 ForEach를 이용해서 list의 값들을 순회할 수도있고, 람다식이 아닌 익명 메서드로도 가능하고... , ForEach 메서드는 Action를 델리게이트 인자로 받는다..

lock 처리


void ThreadFunc(object obj)
{
    MyData data = new();

    for (int i = 0; i < 100; i++)
    {
        lock (data)
        {
            data.Increment();
        }
    }
}

public class MyData
{
    private int number = 0;

    public object _numberLock = new();

    public int Number { get { return number; } }

    public void Increment()
    {
        lock (_numberLock)
        {
            number++;
        }
    }
}
  • 예시이다.. lock을해도되고 try catch.. finally.. + enter 조합으로도 가능하다..

stringBuilder...

  • string을 사용할 때 + 연산이 많은 경우 바로 사용하지 않고 stringBuilder를 사용하는 부분이 많다는 것을 볼 수 있다 해당 이유는 + 연산을 할 때마다 heap 메모리를 잡아 먹을 수 있기때문이다. 그렇게 때문에 builder안에 존재하는 append를 이용하면 heap영역에 메모리를 잡아 먹지 않을 수 있다.


using System.Text;

string txt = "kcj3054";

StringBuilder sb= new();

sb.Append(txt);

for (int i = 0; i < 1000; ++i)
{
    sb.Append('1');
}
  • 위의 표현에서 append로 붙이지않고 1000번의 모든 연산을 +로 한다면 어마어마하게 손실이 발생한다..

종료자..

span

..

  • 출처 : 시작하세요 C# 9.0 프로그래밍..

'c#' 카테고리의 다른 글

semaphore..  (1) 2022.12.06
c# 패턴매칭... switch case에 활용..  (0) 2022.12.06
Generic, where, using static  (0) 2022.12.06
Task vs TaskValue  (0) 2022.12.06
c# (시작하세요 c# 프로그래밍 도서 )  (0) 2022.12.05