Language Feature Status
=====
This document reflects the status, and planned work in progress, for the compiler team. It is a live document
and will be updated as work progresses, features are added / removed, and as work on feature progresses.
This is not an exhaustive list of our features but rather the ones which have active development
efforts behind them.
# Working Set
| Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ |
| ------- | ------ | ----- | --------- | -------- | --------- | --------- |
| [Ref Struct Interfaces](https://github.com/dotnet/csharplang/issues/7608) | [RefStructInterfaces](https://github.com/dotnet/roslyn/tree/features/RefStructInterfaces) | [In Progress](https://github.com/dotnet/roslyn/issues/72124) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | | [agocke](https://github.com/agocke), [jaredpar](https://github.com/jaredpar) |
| [Params-collections](https://github.com/dotnet/csharplang/issues/7700) | [ParamsCollections](https://github.com/dotnet/roslyn/tree/features/ParamsCollections) | [In Progress](https://github.com/dotnet/roslyn/issues/71137) | [AlekseyTs](https://github.com/AlekseyTs) | [RikkiGibson](https://github.com/RikkiGibson), [333fred](https://github.com/333fred) | | [MadsTorgersen](https://github.com/MadsTorgersen), [AlekseyTs](https://github.com/AlekseyTs) |
| [Semi-auto-properties](https://github.com/dotnet/csharplang/issues/140) | [semi-auto-props](https://github.com/dotnet/roslyn/tree/features/semi-auto-props) | [In Progress](https://github.com/dotnet/roslyn/issues/57012) | [Youssef1313](https://github.com/Youssef1313) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) |
| [Params Span\<T> + Stackalloc any array type](https://github.com/dotnet/csharplang/issues/1757) | [params-span](https://github.com/dotnet/roslyn/tree/features/params-span) | [In Progress](https://github.com/dotnet/roslyn/issues/57049) | [cston](https://github.com/cston) | TBD | | [jaredpar](https://github.com/jaredpar) |
| [Default in deconstruction](https://github.com/dotnet/roslyn/pull/25562) | [decon-default](https://github.com/dotnet/roslyn/tree/features/decon-default) | [In Progress](https://github.com/dotnet/roslyn/issues/25559) | [jcouv](https://github.com/jcouv) | [gafter](https://github.com/gafter) | | [jcouv](https://github.com/jcouv) |
| [Roles/Extensions](https://github.com/dotnet/csharplang/issues/5497) | [roles](https://github.com/dotnet/roslyn/tree/features/roles) | [In Progress](https://github.com/dotnet/roslyn/issues/66722) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [jjonescz](https://github.com/jjonescz) | | [MadsTorgersen](https://github.com/MadsTorgersen) |
| [Escape character](https://github.com/dotnet/csharplang/issues/7400) | N/A | [Merged into 17.9p1](https://github.com/dotnet/roslyn/pull/70497) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jcouv](https://github.com/jcouv), [RikkiGibson](https://github.com/RikkiGibson) | | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) |
| [Method group natural type improvements](https://github.com/dotnet/csharplang/blob/main/proposals/method-group-natural-type-improvements.md) | main | [Merged into 17.9p2](https://github.com/dotnet/roslyn/issues/69432) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | | [jcouv](https://github.com/jcouv) |
This file has been truncated. show original
위 링크의 Working Set
이 C# 13 후보 기능입니다. – 목록에 남아 있다가 테스트를 완료하지 못해 다음 버전으로 넘어가기도 합니다.
어느덧 확인이 가능한 기능이 있어서 슬로그를 시작합니다.
11개의 좋아요
C#은 여러 언어에서 벤치마킹 할 정도로 이미 syntax sugar에 대해서 상위에 속하는 언어죠. 오히려 최근 C# 12에 도입된 static interface 에 대해서 논란이 있을 정도로요.
최근에 많이 생각이 드는건, 결국 커뮤니티가 중요한 것 같아요. 이러한 문제들은 프로그래밍 기법으로 언제든지 풀 수 있는 문제들이라고 생각합니다. 언어로만 해결할 수 있는 문제 뿐만 아니라, 프로그래밍 기법들을 만들어 내는 라이브러리들의 부재를 잘 극복해내면 좋겠네요.
오히려 이런 다양한 기능들을 언어적 수준에서 제공하는게 나중에는 rust 처럼 진입장벽을 높이는 효과도 될 수 있고요. golang은 오히려 반대로 언어 수준의 지원을 최소화 하지만 커뮤니티가 잘 발달한 것과 같이요.
7개의 좋아요
이와 별개로 항상 최신 소식들을 전달해주 셔서 @dimohy 님께 감사의 말씀 전달드립니다~
8개의 좋아요
미리보기 기능을 사용하려면 csproj에 다음의 설정을 추가 해야 합니다.
<EnablePreviewFeatures>True</EnablePreviewFeatures>
5개의 좋아요
C# 11의 인터페이스의 정적 추상 메서드 를 말씀하시는 거죠?
어떤 논란이 있었는지 궁금하네요!
5개의 좋아요
rkttu
3월 11, 2024, 11:18오후
6
저도 비슷한 생각입니다. 저 조차도 최신 C# 언어 스펙은 IDE나 보조 도구가 제안해주는 내용을 보고 필요에 따라 조금만 선택해서 사용하는 경우가 많고, 실제로 잘 쓰는 언어 버전은 아무리 높게 쳐줘야 7.x 대 이상으로는 잘 안넘어가는 것 같습니다.
언어에 대한 agressive한 개발보다는 이제는 런타임 기능 강화, 특히 아직은 갈 길이 먼 NativeAOT 관련 기능 개발에 좀 더 신경써주면 좋겠다는 아쉬움이 항상 남는 것 같습니다. ㅎㅎ
6개의 좋아요
이스케이프 문자를 \e
로 사용할 수 있도록 합니다.
char escape_char = '\e';
Assert.IsTrue(escape_char == (char)0x1b, "...");
Assert.IsTrue(escape_char == '\u001b', "...");
Assert.IsTrue(escape_char == '\U0000001b', "...");
Assert.IsTrue(escape_char == '\x1b', "...");
이제 콘솔에서 다음 처럼 이스케이프 문자를 사용할 수 있습니다.
Console.WriteLine("This is a regular text");
Console.WriteLine("\e[1mThis is a bold text\e[0m");
Console.WriteLine("\e[2mThis is a dimmed text\e[0m");
Console.WriteLine("\e[3mThis is an italic text\e[0m");
Console.WriteLine("\e[4mThis is an underlined text\e[0m");
Console.WriteLine("\e[5mThis is a blinking text\e[0m");
Console.WriteLine("\e[6mThis is a fast blinking text\e[0m");
Console.WriteLine("\e[7mThis is an inverted text\e[0m");
Console.WriteLine("\e[8mThis is a hidden text\e[0m");
Console.WriteLine("\e[9mThis is a crossed-out text\e[0m");
Console.WriteLine("\e[21mThis is a double-underlined text\e[0m");
Console.WriteLine("\e[38;2;255;0;0mThis is a red text\e[0m");
Console.WriteLine("\e[48;2;0;255;0mThis is a green background\e[0m");
Console.WriteLine("\e[38;2;0;0;255;48;2;255;255;0mThis is a blue text with a yellow background\e[0m");
5개의 좋아요
오 드디어 params에 컬렉션을 사용할 수 있게 됩니다.
opened 07:09PM - 16 Nov 23 UTC
Proposal champion
# Params Collections
* [x] Proposed: https://github.com/dotnet/csharplang/blo… b/main/proposals/params-collections.md
* [ ] Prototype: Not Started
* [ ] Implementation: Not Started
* [ ] Specification: Not Started
## Summary
[summary]: #summary
In C# 12 language added support for creating instances of collection types beyond just arrays.
See [collection expressions](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md).
This proposal extends `params` support to all such collection types.
This is a placeholder issue for this proposal: https://github.com/dotnet/csharplang/blob/main/proposals/params-collections.md
The proposal subsumes earlier proposals for params spans (#1757) and params IEnumerables (#179).
## Design meetings
* https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-11-15.md#params-improvements
* https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-01-29.md#params-collections
* https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-01-31.md#params-collections-evaluation-orders
* https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-02-21.md#params-collections
17.10p3에 머지 되므로 아직은 테스트해 볼 수는 없습니다.
그리고 이것이 힙 할당 없는 params을 지원하는지는 아직은 모르겠습니다. 별도의 기능으로 Params Span + Stackalloc any array type 이 남아있기 때문입니다.
3개의 좋아요
Params Span + Stackalloc any array type 이 닫혔습니다. Params Collections
에서 구현되었음을 알 수 있네요. 힙 메모리 할당없는 params
의 동작성은 17.10p3에서 확인 가능합니다. (현재는 17.10p2이라 아직 확인할 수는 없습니다.)
3개의 좋아요
iterator와 async 에서 ref 및 unsafe를 허용하는 것이 C# 13에 추가될 것으로 보입니다. 목록에도 추가가 되었네요!
# Allow ref and unsafe in iterators and async
## Summary
[summary]: #summary
Unify behavior between iterators and async methods. Specifically:
- Allow `ref`/`ref struct` locals and `unsafe` blocks in iterators and async methods
provided they are used in code segments without any `yield` or `await`.
- Warn about `yield` inside `lock`.
## Motivation
[motivation]: #motivation
It is not necessary to disallow `ref`/`ref struct` locals and `unsafe` blocks in async/iterator methods
if they are not used across `yield` or `await`, because they do not need to be hoisted.
```cs
async void M()
{
This file has been truncated. show original
6개의 좋아요
메서드 그룹이란 동일한 메서드명을 가진 메서드들을 의미합니다. 다음의 두 링크를 통해 관련 개선을 살펴볼 수 있습니다.
opened 04:29AM - 21 Jul 23 UTC
closed 05:23PM - 16 Oct 23 UTC
Proposal champion
## Determine natural type of method group by looking scope-by-scope
Speclet: … https://github.com/dotnet/csharplang/pull/7399
For reference:
https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md#natural-function-type
> A method group has a natural type if all candidate methods in the method group have a common signature. (If the method group may include extension methods, the candidates include the containing type and all extension method scopes.)
The current behavior leads to an arguably unnecessary error in the following scenario, where an irrelevant extension method (which we have no chance of binding to) causes an error on `z`.
```
System.Action x = c.M; // C.M()
System.Action<object> y = c.M; // E.M(C, object)
var z = new C().M; // the delegate type could not be inferred
public class C
{
public void M() { }
}
public static class E
{
public static void M(this C c, object o) { }
}
```
When the irrelevant extension method has the right signature, we can infer the type of the delegate, but we'll still bind to the instance method (ie. it wins over the extension method).
```
var z = new C().M; // C.M()
public class C
{
public void M() { }
}
public static class E
{
public static void M(this C c) { }
}
```
I would propose that we align the logic that infers the delegate type with the logic that ends up picking which method to bind to.
I would propose the following rule:
> Considering each scope in turn: instance methods, then each scope of extension methods
> If we have a single signature in that scope, then we can infer the function type.
Note that we'll keep the following existing behavior, where we can infer the delegate type but cannot select which method to bind to:
```
var z = new C().M; // ambiguous, but we could infer the delegate type
public class C
{
}
public static class E1
{
public static void M(this C c) { }
}
public static class E2
{
public static void M(this C c) { }
}
```
One advantage of the current design is that it reduces surprises for users who change code from an explicit type to a `var`:
If you start with `System.Action a = c.M;`, the method will have been picked by overload resolution. But when you switch to `var a = c.M;` the type is determined in a different way which can be jarring. So requiring all signatures to match reduces the surprise on the type. But it does not mitigate the surprise on which method we actually bind to...
Relates to test plan https://github.com/dotnet/roslyn/issues/52192 (lambda improvements) and https://github.com/dotnet/roslyn/issues/66722 (extension types)
### Design meetings
* https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-07-24.md#method-group-natural-types-with-extension-members
* https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-10-16.md#determine-natural-type-of-method-group-by-looking-scope-by-scope
opened 11:01PM - 25 Jul 23 UTC
closed 05:29PM - 18 Aug 23 UTC
Bug
Resolution-Duplicate
Area-Compilers
Area-Language Design
New Feature - Lambda Improvements
I wrote this into a [speclet](https://github.com/dotnet/csharplang/pull/7399).
…
The following should not have an error:
```
var x = new C().M<int>; // CS8917 The delegate type could not be inferred.
public class C
{
public void M<T>() { }
public void M<T>(object o) where T : class { }
}
```
Neither should this scenario:
```
System.Action x = new object().M; // okay
var y = new object().M; // CS8917 The delegate type could not be inferred.
static class E1
{
public static void M<T>(this T t) { }
}
static class E2
{
public static void M<T>(this T t) where T : struct { }
}
```
----
We should probably also tweak the arity check so that the following can succeed. We could instead check that no type parameter was left unsubstituted.
```
var d = new C().M; // CS8917 The delegate type could not be inferred.
static class E
{
public static void M<T>(this T t) { }
}
```
That means this scenario would start working:
```
using N;
var d = new C().M;
d();
class C { }
static class E1
{
public static void M<T>(this C c) { } // this extension method would be skipped
}
namespace N
{
static class E2
{
public static void M(this C c) { } // this outer extension method would be picked
}
}
```
Also, should the signature matching check be performed after type argument substitution?
----
In the case where the method group is specified with no type arguments, we'll have no mechanism to infer any type parameters on candidate methods, so generic candidates should be removed.
```
var x = new C().M;
public class C
{
public void M() { }
public void M<T>(T t) { } // this overload has no chance of winning (it would fail even if it were the only overload) so should be pruned early
}
```
----
We may also consider tweaking the notion of "unique signature", as the following scenario currently fails to determine a natural type:
```
var x = new object().F;
static class B
{
internal static void F<T>(this T x) { }
}
static class A
{
internal static void F(this object x) { }
}
```
Follow-up on https://github.com/dotnet/csharplang/pull/7380 (change to look scope-by-scope when determining the natural type of a method group)
2개의 좋아요
개체 이니셜라이저에서 암시적 인덱서 접근 (Implicit indexer access in object initializers) 17.9p3 적용됨
다음의 코드 처럼
var t = new T();
t[^1] = 1;
class T
{
private readonly int[] _data = new int[10];
public int Length => _data.Length;
public int this[int index]
{
get => _data[index];
set => _data[index] = value;
}
}
Index
로 받지 않아도 Length
를 속성으로 반환하면 인덱스 접근이 가능했습니다.
하지만 개체 이니셜라이저를 통해서는 컴파일 오류가 발생했는데요,
17.9p3 부터 컴파일 오류 없이 사용 가능합니다.
3개의 좋아요
이제 lock
문에서 사용할 수 있는 개체는 object
뿐만 아니라 System.Threading.Lock
개체가 될 수 있습니다.
private Lock @lock = new Lock();
...
lock (@lock)
{
....
}
Lock
을 사용하는 lock
문은 Lock
에서 제공하는 락 진입 및 해제를 그대로 사용하게 됩니다.
class MyDataStructure
{
private readonly Lock _lock = new();
void Foo()
{
lock (_lock)
{
// do something
}
}
}
class MyDataStructure
{
private readonly Lock _lock = new();
void Foo()
{
using (_lock.EnterLockScope())
{
// do something
}
}
}
System.Threading.Lock
의 동작은 기존 lock
문의 동작과 구현이 다르며 앞으로 좀 더 최적화 될 여지가 있습니다.
bool IEquatable<State>.Equals(State other) => this == other;
public override bool Equals(object? obj) => obj is State other && this == other;
public override int GetHashCode() => (int)_state;
private static State CompareExchange(Lock lockObj, State toState, State fromState) =>
new State(Interlocked.CompareExchange(ref lockObj._state, toState._state, fromState._state));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryLock(Lock lockObj)
{
// The lock is mostly fair to release waiters in a typically FIFO order (though the order is not guaranteed).
// However, it allows non-waiters to acquire the lock if it's available to avoid lock convoys.
//
// Lock convoys can be detrimental to performance in scenarios where work is being done on multiple threads and
// the work involves periodically taking a particular lock for a short time to access shared resources. With a
// lock convoy, once there is a waiter for the lock (which is not uncommon in such scenarios), a worker thread
// would be forced to context-switch on the subsequent attempt to acquire the lock, often long before the worker
// thread exhausts its time slice. This process repeats as long as the lock has a waiter, forcing every worker
// to context-switch on each attempt to acquire the lock, killing performance and creating a positive feedback
// loop that makes it more likely for the lock to have waiters. To avoid the lock convoy, each worker needs to
// be allowed to acquire the lock multiple times in sequence despite there being a waiter for the lock in order
2개의 좋아요
드디어 params 컬렉션을 테스트 할 수 있게 되었습니다. 이제 다음의 코드는 정상적인 코드이며 잘 실행됩니다.
PrintReadOnlySpanParams(1, 2, 3, 4, 5);
PrintListParams(1, 2, 3, 4, 5);
PrintEnumerableParams(1, 2, 3, 4, 5);
void PrintReadOnlySpanParams(params ReadOnlySpan<int> @params)
{
foreach (var p in @params)
{
Console.WriteLine(p);
}
}
void PrintListParams(params List<int> @params)
{
foreach (var p in @params)
{
Console.WriteLine(p);
}
}
void PrintEnumerableParams(params IEnumerable<int> @params)
{
foreach (var p in @params)
{
Console.WriteLine(p);
}
}
params ReadOnlySpan<>
을 이용하면 가변 인자를 넘기기 위해 불필요하게 힙 할당할 필요가 없어집니다.
6개의 좋아요
이전에는 소스 생성기를 통해 속성을 생성하고 싶어도 부분 속성이 없었기 때문에 다음 처럼 해야 했습니다.
[NotifyPropertyChanged]
private string _userName;
생성되는 코드
public partial string UserName
{
get => _userName;
set
{
if (value != _userName)
{
_userName = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName)));
}
}
}
이제 부분 속성 (Partial properties) 에 의해 다음처럼 사용할 수 있습니다.
[NotifyPropertyChanged]
public partial string UserName { get; set; }
생성되는 코드
private string __generated_userName;
public partial string UserName
{
get => __generated_userName;
set
{
if (value != __generated_userName)
{
__generated_userName = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName)));
}
}
}
7개의 좋아요
field
키워드는 이번에도 결국 반영되지 않았네요…
opened 09:53PM - 17 Feb 17 UTC
Proposal champion
Feature Request
# Proposal: `field` keyword in properties
(Ported from [dotnet/roslyn#8364](htt… ps://github.com/dotnet/roslyn/issues/8364))
Specification: https://github.com/dotnet/csharplang/blob/main/proposals/field-keyword.md
## LDM history:
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-03-10.md#field-keyword
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-04-14.md#field-keyword
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-12.md#open-questions-for-field
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-16.md#open-questions-in-field
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-03-02.md#open-questions-in-field
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-03-21.md#open-question-in-semi-auto-properties
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-05-02.md#field-questions
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-26.md#ungrouped
- https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-10-12.md#keywordness-of-field
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-05-15.md#field-and-value-as-contextual-keywords
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-06-24.md#field-questions
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-07-15.md#field-keyword
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-08-14.md#field-keyword
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-08-21.md#field-keyword-nullability
아니 이거 구현은 3~4년 전 부 터 진행되었던 것 같은데 반영이 오래 걸리네요…
3개의 좋아요
아래 코드를 실행하면 Array
가 찍히는데요, OverloadResolutionPriority
특성을 부여해서 Span
이 찍히도록 조절할 수 있습니다.
var d = new C1();
int[] arr = [1, 2, 3];
d.M(arr); // Prints "Span"
class C1
{
public void M(ReadOnlySpan<int> s) => Console.WriteLine("Span");
// Default overload resolution priority
public void M(int[] a) => Console.WriteLine("Array");
}
| 출력
Array
using System.Runtime.CompilerServices;
var d = new C1();
int[] arr = [1, 2, 3];
d.M(arr); // Prints "Span"
class C1
{
[OverloadResolutionPriority(1)]
public void M(ReadOnlySpan<int> s) => Console.WriteLine("Span");
// Default overload resolution priority
public void M(int[] a) => Console.WriteLine("Array");
}
| 출력
Span
이는 오버로드 메소드의 호출 우선순위를 조정하고자 할 때 유용해 보입니다.
2개의 좋아요