들어가며
참고
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history
C# version | |
---|---|
12 | whats-new |
11.0 | whats-new |
10.0 | whats-new |
9.0 | |
8.0 | |
7.3 | |
7.2 | |
7.1 | |
7.0 | |
6.0 | |
5.0 | |
4.0 | C# 4.0 - New C# Features in the .NET Framework 4 |
3.0 | |
2.0 | What's New in the C# 2.0 Language and Compiler |
1.2 | |
1.0 |
C# version | VS version | .NET version | CLR version | Release date | End of Support | Unity |
---|---|---|---|---|---|---|
12 | 2022 (17.8) | .NET 8 | . | Nov 2023 | Nov 2026 | |
11 | 2022 (17.4) | .NET 7 | . | Nov 2022 | May 2024 | |
10 | 2022 | .NET 6 | . | Nov 2021 | Nov 2024 | |
9 (†) | 2019 (v16.8) | .NET 5 | . | Nov 2020 | May 2022 | 2021.2 |
2019 (v16.4) | .NET Core 3.1 | . | Dec 2019 | Dec 2022 | ||
2019 (v16.3) | .NET Core 3.0 | . | Sep 2019 | Mar 2020 | ||
8.0 | 2019 | . | Apr 2019 | |||
.NET standard 2.2 | . | Dec 2018 | Dec 2019 | 2020.2 | ||
7.3 | 2017 (v15.7) | .NET standard 2.1 | . | May 2018 | Aug 2021 | |
7.1 | 2017 (v15.3) | .NET standard 2.0 | . | Aug 2017 | Oct 2018 | |
7.0 | 2017 | . | Mar 2017 | |||
.NET standard 1.1 | . | Nov 2016 | Jun 2019 | |||
2015 Update 3 | .NET standard 1.0 | . | Jun 2016 | Jun 2019 | ||
== .NET Core | == .NET Core | == .NET Core | == .NET Core | == .NET Core | == .NET Core | |
4.8.1 | 4 | Aug 2022 | . | |||
8.0 | 2019 | 4.8 | 4 | Apr 2019 | . | |
7.3 | 2017 (v15.7) | May 2018 | . | |||
4.7.2 | 4 | Apr 2018 | . | |||
7.2 | 2017 (v15.5) | Dec 2017 | . | |||
4.7.1 | 4 | Oct 2017 | . | |||
7.1 | 2017 (v15.3) | Aug 2017 | . | |||
4.7 | 4 | May 2017 | . | |||
7.0 | 2017 | Mar 2017 | . | |||
4.6.2 | 4 | Aug 2016 | . | |||
4.6.1 | 4 | Nov 2015 | . | |||
6.0 | 2015 | 4.6 | 4 | Jul 2015 | . | |
4.5.2 | 4 | May 2014 | . | |||
2013 | 4.5.1 | 4 | Oct 2013 | . | ||
5.0 | 2012 | 4.5 | 4 | Aug 2012 | . | |
4.0 | 2010 | 4.0 | 4 | Apr 2010 | . | |
3.0 | 2008 | 3.5 | 2.0 | Nov 2007 | . | |
3.0 | 2.0 | Nov 2006 | . | |||
2.0 | 2005 | 2.0 | 2.0 | Nov 2005 | . | |
1.2 | 2003 | 1.1 | 1.1 | Apr 2003 | . | |
1.0 | 2002 | 1.0 | 1.0 | Feb 2002 | . |
- https://stackoverflow.com/questions/247621/what-are-the-correct-version-numbers-for-c
- https://www.c-sharpcorner.com/article/c-sharp-versions/
9.0
top level statement
- https://learn.microsoft.com/en-us/dotnet/core/tutorials/top-level-templates
--use-program-main
<PropertyGroup> <ImplicitUsings>disable</ImplicitUsings> </PropertyGroup>
<ItemGroup> <Using Remove="System.Net.Http" /> </ItemGroup>
Tool
$ dotnet new tool-manifest
$ cat .config/dotnet-tools.json
$ dotnet tool install dotnet-format
$ cat .config/dotnet-tools.json
- dotnet-format
- coverlet.console
- dotnet-reportgenerator-globaltool
- microsoft.dotnet.xharness.cli
- microsoft.visualstudio.slngen.tool
Ref
- https://www.nuget.org/packages?packagetype=dotnettool
- https://github.com/dotnet/runtime/blob/main/.config/dotnet-tools.json
Source Generator
- 새 코드를 추가 가능.
- 기존 코드 수정 불가능
- C# 혹은 추가 파일에 접근 가능
Unity
- 버전
- .NET 버전
- IDE의 컴파일러 버전
- Microsoft.CodeAnalysis.CSharp 버전
- /Editor/Data/DotNetSdkRoslyn/Microsoft.CodeAnalysis.CSharp.dll 버전보다 높은 버전의 것을 참조하면 움직이지 않는다.
Roslyn - 4.3.1 Visual Studio 2022 - 17.3
.NET Compiler Platform SDK
-
etc: Improved Interpolated Strings가 C# 10.0
-
빌드된.dll
- 의존성 관리가 힘들 수 도 있으니 하나의 dll로 만드는게 좋음.
- label: RoslynAnalyzer
- dll배치는 Runtime/ 쪽에 놔두는 경향이 있음
ISourceGenerator & IIncrementalGenerator
- deprecated - ISourceGenerator: 전체 코드 베이스
- IIncrementalGenerator: 변경된 부분
Microsoft.CodeAnalysis.CSharp | Unity | |
---|---|---|
3.x | ISourceGenerator | |
4.x | IIncrementalGenerator | 2022.2~, 2023.1~ |
Sample: SourceGenerator
mkdir SourceGenerator
cd SourceGenerator
dotnet new gitignore
dotnet new classlib -o SourceGeneratorGen
dotnet new console -o SourceGeneratorSrc
dotnet new sln
dotnet sln add SourceGeneratorGen
dotnet sln add SourceGeneratorSrc
SourceGeneratorGen
- 우클릭 소스제너레이터 프로젝트
- Properties > Debug > Open debug launch profiles UI
- 보이는 프로파일 삭제
- 추가 클릭
- Roslyn component 선택
- 타겟 프로젝트에서 콘솔 어플리케이션 프로젝트 선택
- UI 닫기
- 비주얼 스튜디오 재시작
- 재생 버튼 옆 디버그 프로파일 드롭다운에서 소스제너레이터 프로젝트 선택
- 디버거가 멈추는지 확인하기 위해 소스제너레이터에 브레이크 포인트를 설정
- 재생 클릭
<!-- SourceGeneratorGen/SourceGeneratorGen.csproj-->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<LangVersion>11</LangVersion>
<!-- IsRoslynComponent: true => | Properties > Debug > Launch > Roslyn Component -->
<IsRoslynComponent>true</IsRoslynComponent>
<AnalyzerLanguage>cs</AnalyzerLanguage>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
</ItemGroup>
</Project>
// SourceGeneratorGen/Generator.cs
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;
namespace SourceGeneratorGen
{
[Generator(LanguageNames.CSharp)]
public sealed class Generator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// === IncrementalValue[s]Provider<T>
//context.CompilationProvider
//context.AdditionalTextsProvider
//context.AnalyzerConfigOptionsProvider
//context.MetadataReferencesProvider
//context.ParseOptionsProvider
// === SyntaxValueProvider
// context.SyntaxProvider
// === Outputting values
//context.RegisterSourceOutput // 사용자 컴파일에 포함될 소스 파일과 진단을 생성할 수 있다
//context.RegisterImplementationSourceOutput // RegisterSourceOutput랑 비슷. 단, 유저코드나 다른 변환기에 의해 실행되지 않음. 코드 분석에 영향을 주지 않음.
//context.RegisterPostInitializationOutput // 다른 변환이 실행되기 전에 컴파일에 포함됨
context.RegisterPostInitializationOutput(Callback);
IncrementalValuesProvider<GeneratorAttributeSyntaxContext> source = context.SyntaxProvider.ForAttributeWithMetadataName(
fullyQualifiedMetadataName: "GeneratedNamespace.GenerateToStringAttribute",
predicate: static (node, token) => true,
transform: static (context, token) => context);
context.RegisterSourceOutput(source, Emit);
}
private void Callback(IncrementalGeneratorPostInitializationContext context)
{
string code = """
using System;
namespace GeneratedNamespace
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
internal sealed class GenerateToStringAttribute : Attribute
{
}
}
""";
context.AddSource("Generated.cs", code);
}
private void Emit(SourceProductionContext context, GeneratorAttributeSyntaxContext source)
{
INamedTypeSymbol typeSymbol = (INamedTypeSymbol)source.TargetSymbol;
TypeDeclarationSyntax typeNode = (TypeDeclarationSyntax)source.TargetNode;
if (typeSymbol.GetMembers("ToString").Length != 0)
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ExistsOverrideToString, typeNode.Identifier.GetLocation(), typeSymbol.Name));
return;
}
string ns;
if (typeSymbol.ContainingNamespace.IsGlobalNamespace)
{
ns = string.Empty;
}
else
{
ns = $"{typeSymbol.ContainingNamespace}";
}
string fullType = typeSymbol
.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
.Replace("global::", "")
.Replace("<", "_")
.Replace(">", "_");
IEnumerable<string> publicMembers = typeSymbol
.GetMembers()
.Where(x => x is (IFieldSymbol or IPropertySymbol)
and { IsStatic: false, DeclaredAccessibility: Accessibility.Public, IsImplicitlyDeclared: false, CanBeReferencedByName: true })
.Select(x => $"{x.Name}:{{{x.Name}}}"); // MyProperty:{MyProperty}
string toString = string.Join(", ", publicMembers);
// multiline string interpolation
string code = $$"""
// ========================== auto-generated
#pragma warning disable CS8600
#pragma warning disable CS8601
#pragma warning disable CS8602
#pragma warning disable CS8603
#pragma warning disable CS8604
namespace {{ns}}
{
partial class {{typeSymbol.Name}}
{
public override string ToString()
{
return $"{{toString}}";
}
}
}
#pragma warning restore CS8604
#pragma warning restore CS8603
#pragma warning restore CS8602
#pragma warning restore CS8601
#pragma warning restore CS8600
""";
context.AddSource($"{fullType}.SampleGenerator.g.cs", code);
}
}
public static class DiagnosticDescriptors
{
private const string CATEGORY = "GeneratedNamespace";
public static readonly DiagnosticDescriptor ExistsOverrideToString = new DiagnosticDescriptor(
id: "SAMPLE001",
title: "ToString override",
messageFormat: "The GenerateToString class '{0}' has ToString override but it is not allowed",
category: CATEGORY,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true
);
}
}
SourceGeneratorSrc
<!-- SourceGeneratorSrc/SourceGeneratorSrc.csproj -->
<!-- TODO: https://stevetalkscode.co.uk/debug-source-generators-with-vs2019-1610 -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SourceGeneratorGen\SourceGeneratorGen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
// SourceGeneratorSrc/Program.cs
using GeneratedNamespace;
using System;
namespace SourceGeneratorSrc
{
internal class Program
{
private static void Main(string[] args)
{
MyClass mc = new MyClass() { Hoge = 10, Bar = "tako" };
Console.WriteLine(mc);
}
}
[GenerateToString]
public partial class MyClass
{
public int Hoge { get; set; }
public string Bar { get; set; }
}
}
Etc
// IDE 경고내기
private const string CATEGORY = "GeneratedNamespace";
public static readonly DiagnosticDescriptor ExistsOverrideToString = new DiagnosticDescriptor(
id: "SAMPLE001",
title: "ToString override",
messageFormat: "The GenerateToString class '{0}' has ToString override but it is not allowed",
category: CATEGORY,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true
);
SourceProductionContext context;
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ExistsOverrideToString, typeNode.Identifier.GetLocation(), typeSymbol.Name));
ReportDiagnostic Enum | ||
---|---|---|
Default | 0 | Report a diagnostic by default. |
Error | 1 | Report a diagnostic as an error. |
Warn | 2 | Report a diagnostic as a warning even though /warnaserror is specified. |
Info | 3 | Report a diagnostic as an info. |
Hidden | 4 | Report a diagnostic as hidden. |
Suppress | 5 | Suppress a diagnostic. |
로슬린
컴파일
Ref
-
https://docs.unity3d.com/Manual/roslyn-analyzers.html
- Microsoft.CodeAnalysis 3.8
- 문서상 3.8로 되어있지만 3.9로 해야함.
-
- deprecated - ISourceGenerator
- Source Generators Cookbook -IIncrementalGenerator
- Incremental Generators
- deprecated - ISourceGenerator
-
정성태
-
neue.cc
-
Roslyn package version & Minimum supported Visual Studio version
-
https://andrewlock.net/exploring-dotnet-6-part-9-source-generator-updates-incremental-generators/
-
Set as Startup Project 간단히 : project 이름 > right click >
a
-
- static lamda면 외부 context를 캡쳐하지 않는다.
Source Generator ex
- Series: Creating a source generator
- Part 1 - Creating an incremental generator
- Part 2 - Testing an incremental generator with snapshot testing
- Part 3 - Integration testing and NuGet packaging
- Part 4 - Customising generated code with marker attributes
- Part 5 - Finding a type declaration's namespace and type hierarchy
- Part 6 - Saving source generator output in source control
- Part 7 - Solving the source generator 'marker attribute' problem - Part 1
- Part 8 - Solving the source generator 'marker attribute' problem - Part 2
Analyzer
- roslyn-analyzers
- https://learn.microsoft.com/en-us/visualstudio/code-quality/roslyn-analyzers-overview
- FXCopAnalyzers
- StyleCopAnalyzers
- Security Code Scan
- Roslynator
- AsyncFixer
- Meziantou.Analyzer
- SerilogAnalyzer
- Microsoft.AspNetCore.Mvc.Api.Analyzers
- SonarAnalyzer.CSharp
- NSubstitute.Analyzers.CSharp
- xunit.analyzers
- Microsoft.CodeQuality.Analyzers
- Microsoft.CodeAnalysis.VersionCheckAnalyzer
- ReSharper Command Line Tools