ValueTask 암시적 형 변환 여부

await async 메소드에서 리턴 타입이 ValueTask 일 때, 꼭 return new ValueTask(Enum) 으로 감싸주지 않고 바로 return Enum 해도 완전히 똑같은가요?
예를 들면 아래와 같은 경우… 두 경우 모두 완전히 같은 결과인지 궁금하네요 (CLR 인터프리터 코드 보면 알 수 있겠지만 확인차…)

public async ValueTask < EnumReturnType > TestReturnValueTask()
{
return EnumReturnType.Success; // 암시적 형 변환을 기대
// return new ValueTask < EnumReturnType > (EnumReturnType.Success); // 명시적으로 할당하지 않아도 암시적 형 변환과 동일한 결과를 기대
}

사용하려는 이유는 async Task 대신 ValueTask로 gc 빈도를 줄이려는 것이고 문법상 슈가 신택스가 가능한지, 그리고 그 경우 오버헤드는 없는지 궁금합니다

https://www.sysnet.pe.kr/2/0/13115

이 게시물을 읽고 드린 질문입니다

리턴타입이 public async ValueTask 일 경우에

return new ValueTask(value); <= 원래 문법
return value; <= 쓰고 싶은 문법

아래 슈가 신택스로 이용해도 차이가 없는 것인지가 궁금했습니다

ValueTask및 Task는 명시적/암시적 형변환을 하지 않습니다.

async 메소드일 경우 반환값이 ValueTask/Task가 아니라 await에 의해 비동기 처리의 결과 값을 얻을 수 있으므로 ValueTask/Task의 제네릭 유형을 반환하는 형태로 C# 언어가 해석해줍니다.

async ValueTask TestReturnValueTask()
{
    return EnumReturnType.Success;
}

이것은 제네릭 유형이 없으므로 오류 입니다. 아마도 ```코드``` 형태로 코드가 공유되지 않아 제네릭 <type>이 편집기에서 생략된 듯 합니다.

async ValueTask<EnumReturnType> TestReturnValueTask()
{
    return EnumReturnType.Success;
}

정상 동작할 것이지만 컴파일러는 await가 없어서 CS1998 경고를 발생합니다. await가 없으면 async 키워드는 필요가 없습니다.

async ValueTask<EnumReturnType> TestReturnValueTask()
{
    return new ValueTask(EnumReturnType.Success);
}

ValueTask가 형변환을 지원하지 않으므로 오류가 발생합니다.

ValueTask<EnumReturnType> TestReturnValueTask()
{
    return ValueTask.FromResult(EnumReturnType.Success);
}

원하시는 형태는 아마 이것일 것 같습니다. FromResult 메소드는 값을 ValueTask로 만들어줍니다.

3개의 좋아요

상세한 답변에 진심으로 감사드립니다. 글을 처음 써서 올리는거라 코드 제네릭 꺾쇠가 지워져서 올라간 걸 나중에서야 확인했네요. 많은 도움이 되었습니다 감사합니다.

실제 코드에는 중간에 db save 등과 같은 async await 메소드가 포함되어있습니다. 그래서 async 메소드 안에서 new ValueTask(value) 또는 ValueTask.FromResult(value) 대신 바로 value를 리턴해도 상관 없는지 여부가 궁금했습니다

채팅봇에게 받은 답변을 추가로 올립니다

async 메소드의 리턴 타입으로 ValueTask를 사용할 때, 일반적으로는 ValueTask.FromResult(result)를 사용하여 ValueTask 객체를 만들어 리턴합니다. 이렇게 하면 메모리 할당을 방지하고 성능을 향상시킬 수 있습니다. 하지만 만약에 메소드의 구현에서 ValueTask 객체를 사용하여 반환하지 않고, 일반 데이터 타입을 즉시 반환한다면, CLR은 내부적으로 암시적으로 ValueTask 객체로 형변환을 수행합니다.
즉, 일반 데이터 타입을 반환하는 경우 암시적 형변환이 발생하므로 명시 적으로 ValueTask를 할당할 필요는 없습니다. 수행 결과는 기본적으로 ValueTask로 형변환됩니다. 이 경우 CLR은 ValueTask.Builder를 사용하여 새로운 ValueTask 객체를 생성하지 않고 원본 Task를 사용합니다.
이러한 경우 반면에 ValueTask를 반환하는 경우, 새로운 ValueTask 객체를 할당해야합니다. 반환된 ValueTask 객체에는 스택 프레임에있는 것과 System.Threading.Tasks.ValueTaskCompletionSource 개체가 할당되며 반환값의 참조 카운트를 추적하는 데 사용됩니다.
결론적으로, ValueTask를 반환하는 경우 새로운 ValueTask 객체를 할당하여 반환하는 것이 좋습니다. 그러나 일반 데이터 타입을 반환하는 경우, 성능상의 이유로 불필요한 짓은 하지 않아도 됩니다.

채팅봇의 답변을 너무 참고하면 안됩니다.

채팅봇이 답변한 내용 중 50% 정도는 사실이 아닙니다.

image

  • 명시/암시 operator 구현이 없습니다.
  • TResult 유형을 받는 생성자가 없습니다.
2개의 좋아요

아 불확실한 정보를 올려서 잘못된 내용을 퍼트려서 죄송합니다 확인해주셔서 감사합니다

1개의 좋아요

"쓰고 싶은 문법"이 아니라, “써야 하는 문법” 입니다.

“원래 문법” 대로 하려면, 리턴 타입에 async 가 빠져야 합니다.

2개의 좋아요

아…;;;;, 그렇네요 제가 처음부터 틀린 질문을 했네요

Screenshot_20230623-144920~2

다음부턴 더 신중하게 확인해보고 질문하도록 하겠습니다 감사합니다

2개의 좋아요

async 는 최대한 미루는 게 슈가 코드입니다.

public ValueTask<Person> GetById(int id)
{
    return dbcontext.People.FindAsync(id);
}

다만, 내부에 동기적으로 수행해야 할 또 다른 비동기 작업이 있을 경우에는 반드시 Async 를 써야 합니다.

public async ValueTask<Student?> GetByPersonId(int personId)
{
    var person =  await dbcontext.People.FindAsync(id); // 먼저 수행해야 하는 비동기 작업
    if(person == null)
       return null;

    return (await dbcontext.Students.Where(x => x.Name == person.Name))?.FirstOrDefault();
}
1개의 좋아요

좀 다른 얘기지만 슈가 신택스라는 말을 처음 들어봅니다
기능상 문제는 없지만 보기좋은 code라는 의미인가요??
기존 가독성이랑 좀 다른 개발자 성향에 따른 코드라고 하는것 같긴 하네요

1개의 좋아요

Syntax Sugar(설탕 문법) : 컴파일러가 대체해서 관련 수행 코드를 생성해 주는 의미 입니다.

대표적으로 async / await 키워드는 컴파일 시점에 상태머신 코드(Task가 사용 된다면) 및 관련 클래스 등을 자동으로 생성해서 비동기 적으로 동작 되도록 합니다.

2개의 좋아요

이 말 한번 자세히 설명 부탁드릴 수 있을까요?

async 키워드를 최대한 쓰지 않는게 좋다는 의미신가요?

아니면 async 키워드를 불필요하게 사용했을 때 @aroooong 님이 말씀하신
상태머신 코드가 불필요하게 생기게 된다는 말씀이신가요?

정답입니다. 깡통 머신이 계속 양산됩니다.

아래와 같은 async 메서드를 Data 레이어에서 정의하면, 이를 소비하는 다른 레이어에서도 async/await 을 할 수 밖에 없는데, 이는 기다리기만 하는 상태 머신만 계속 양산하는 결과를 낳습니다.

따라서, async 는 가급적 최종 소비 레이어, 예를 들면, UI 레이어에서 수행되어야, 딱 필요한 만큼의 상태 머신을 소비합니다.

public async ValueTask<Student?> GetByPersonId(int personId)
{
    var person =  await dbcontext.People.FindAsync(id); // 먼저 수행해야 하는 비동기 작업
    if(person == null)
       return null;

    return (await dbcontext.Students.Where(x => x.Name == person.Name))?.FirstOrDefault();
}
1개의 좋아요

아 네 ㅋㅋ 그건 그렇죠 ㅋㅋ
하지만 함수 시그니처에 async를 붙이고 바디에 await 키워드를 사용하지 않았을 경우 @dimohy 님이 아래 말씀하신 것 처럼 경고가 떠서 불필요하게 async를 함수에 붙이는 일이 있을까 싶기는 하네요 :joy:

1개의 좋아요

@dimohy 님의 말씀은 문제가 있는 코드이니, 쓰지 마라는 의도가 아닐런지요 ^^;;

고 밑에 밑에 정상적인 코드도 병기해 놓으셨네요