coding-conventions
npx skills add https://github.com/icartsh/icartsh_plugin --skill coding-conventions
Agent 安装分布
Skill 文档
Coding Conventions
ê°ì
ì´ SKILLì ê°ë°ëë 모ë .NET íë¡ì í¸ì ì ì©ëë ì½ë© ê·ì½ì ì ìí©ëë¤. .NET 8 ì´íì ìµì 기ë¥(C# 12/13/14, .NET 8/9/10)ì ì ê·¹ì ì¼ë¡ íì©íì¬ ê°ë ì±, ì ì§ë³´ìì±, ì±ë¥ì´ ëì ì½ë를 구ííë ê²ì 목ì ì¼ë¡ í©ëë¤.
ì± ì ë²ì
ì´ SKILLì ë¤ì ë²ì를 ë¤ë£¹ëë¤:
- .NET/C#ì ìµì 기ë¥(C# 12/13/14, .NET 8/9/10) íì© ë°©ì¹¨
- ëª ëª ê·ì¹ (Type, Member, Variable, Parameter)
- ì½ë ë ì´ìì ë° í¬ë§·
- ì¸ì´ ê¸°ë¥ ì¬ì© 방침 (Type inference, Collection, Exception handling)
- LINQì ëë¤ìì ëª¨ë² ì¬ë¡
- 모ë C# 구문ì ê¶ì¥ í¨í´
기본 방침
- .NET 8 ì´íì ìµì 기ë¥ì ì ê·¹ì ì¼ë¡ ì¬ì©íë¤ (C# 12/13/14, .NET 8/9/10)
- ì´ì ë²ì ê³¼ì í¸íì±ì´ íìí ê²½ì°ë¥¼ ì ì¸íê³ íì ìµì 기ë¥ì ì°ì íë¤
- ì¤ëë ì¸ì´ 구문ì í¼íë¤
- ì¤ì: ì¸ëì¤ì½ì´ ì ëì¬(
_field) ì¬ì©ì ì ë ê¸ì§íë¤ - ì¤ì: ì¤ê´í¸ ìëµì ì ë ê¸ì§íë¤ (1íì¼ë¡ 기ì í ì ìë ê²½ì°ìë ìëµ ë¶ê°)
- Microsoft ê³µì ì½ë© ê·ì½ì ë°ë¥¸ë¤
- ì¼ê´ì±ì ì ì§íê³ í ì ì²´ìì ëì¼í ì¤íì¼ì ì ì©íë¤
.NET/C# ìµì ê¸°ë¥ (ë²ì ë³)
.NET 8 ì´í ê° ë²ì ìì ëì ë 주ì 기ë¥ì ì ê·¹ì ì¼ë¡ íì©í©ëë¤. ì´ì ë²ì ê³¼ì í¸íì±ì´ íìí ê²½ì°ë¥¼ ì ì¸íê³ íì ìµì 기ë¥ì ì°ì ì ì¼ë¡ ì¬ì©í©ëë¤.
C# 12 (.NET 8) – 2023ë 11ì ê³µì 릴리ì¤
Primary Constructors
- í´ëì¤ë struct ì ì¸ìì íë¼ë¯¸í°ë¥¼ ì ìíê³ í´ëì¤ ì ì²´ìì ì¬ì©í ì ìì
- ëª ìì ì¸ íë ì ì¸ì ì¤ì´ê³ ì´ê¸°í를 ê°ìíí¨
ì¢ì ì:
public class Person(string name, int age)
{
public string Name => name;
public int Age => age;
public void Display()
{
Console.WriteLine($"{name} is {age} years old");
}
}
ëì ì:
public class Person
{
private string name;
private int age;
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
public string Name => name;
public int Age => age;
}
Collection Expressions
- ëê´í¸ì ì¤íë ë ì°ì°ì를 ì¬ì©íì¬ ì»¬ë ì ì ê°ê²°íê² ìì±í¨
- ì¬ë¬ 컬ë ì ì ê²°í©í ë ì ì©í¨
ì¢ì ì:
int[] array = [1, 2, 3, 4, 5];
List<string> list = ["one", "two", "three"];
int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
// ì¤íë ë ì°ì°ìë¡ ê²°í©
int[] combined = [..row0, ..row1];
Default Lambda Parameters
- ëë¤ìì 기본 íë¼ë¯¸í° ê°ì ì§ì í ì ìì
ì¢ì ì:
var incrementBy = (int source, int increment = 1) => source + increment;
Console.WriteLine(incrementBy(5));
Console.WriteLine(incrementBy(5, 3));
Alias Any Type
- using ëë í°ë¸ë¡ ë³µì¡í íì ì ë³ì¹ì ë¶ì¼ ì ìì
ì¢ì ì:
using Point = (int x, int y);
using ProductList = System.Collections.Generic.List<(string Name, decimal Price)>;
Point origin = (0, 0);
ProductList products = [("Product1", 100m), ("Product2", 200m)];
C# 13 (.NET 9) – 2024ë 11ì ê³µì 릴리ì¤
Params Collections
paramsììì´ë¥¼ ë°°ì´ ì¸ì 컬ë ì íì ììë ì¬ì© ê°ë¥í´ì§List<T>,Span<T>,ReadOnlySpan<T>,IEnumerable<T>ë±ìì ì¬ì© ê°ë¥
ì¢ì ì:
public void ProcessItems(params List<string> items)
{
foreach (var item in items)
{
Console.WriteLine(item);
}
}
// ë©ëª¨ë¦¬ í¨ì¨ì´ ì¤ìí ê²½ì°
public void ProcessData(params ReadOnlySpan<int> data)
{
foreach (var value in data)
{
Process(value);
}
}
New Lock Type
System.Threading.Lockíì ì ì¬ì©íì¬ ë ë¹ ë¥¸ ì¤ë ë ë기í를 구íí¨- 기존
Monitorê¸°ë° ë½ë³´ë¤ ë¹ ë¦
ì¢ì ì:
private readonly Lock lockObject = new();
public void UpdateData()
{
lock (lockObject)
{
// Critical section
}
}
ëì ì:
// 기존 object ê¸°ë° ë½ (C# 13ììë ê¶ì¥ëì§ ìì)
private readonly object lockObject = new();
public void UpdateData()
{
lock (lockObject)
{
// Critical section
}
}
Partial Properties and Indexers
- partial íë¡í¼í°ì ì¸ë±ì를 ì¬ì©í ì ìê² ë¨
- ì ìì 구íì ë¶ë¦¬í ì ìì
ì¢ì ì:
// ì ì ë¶ë¶
public partial class DataModel
{
public partial string Name { get; set; }
}
// 구í ë¶ë¶
public partial class DataModel
{
private string name;
public partial string Name
{
get => name;
set => name = value ?? throw new ArgumentNullException(nameof(value));
}
}
Implicit Index Access
- ê°ì²´ ì´ëì
ë¼ì´ì ìì
^ì°ì°ì를 ì¬ì©í ì ìê² ë¨
ì¢ì ì:
var countdown = new TimerBuffer
{
buffer =
{
[^1] = 0,
[^2] = 1,
[^3] = 2
}
};
Ref Struct Enhancements
ref structíì ì´ ì¸í°íì´ì¤ë¥¼ 구íí ì ìê² ë¨- ì ë¤ë¦ íì
ìì
ref struct를 ì¬ì©í ì ìê² ë¨ (allows ref structì ì½ ì¡°ê±´)
ì¢ì ì:
public ref struct SpanWrapper<T> : IEnumerable<T>
{
private Span<T> span;
public IEnumerator<T> GetEnumerator()
{
foreach (var item in span)
{
yield return item;
}
}
}
C# 14 (.NET 10) – 2025ë 11ì ë¦´ë¦¬ì¤ ìì
Extension Members
- Extension Members를 íì©íì¬ ê¹ëí API íì¥ì 구íí¨
- ìë íì ì ì¤ì¼ìí¤ì§ ìê³ ê¸°ë¥ì ì¶ê°í ì ìì
ì¢ì ì:
extension<TSource>(IEnumerable<TSource> source)
{
public bool IsEmpty => !source.Any();
public int Count => source.Count();
}
Field-Backed Properties
fieldí¤ìë를 ì¬ì©íì¬ ëª ìì ì¸ backing field를 ì¤ì- ê²ì¦ ë¡ì§ì ê°ê²°íê² ê¸°ì í ì ìì
- ì¸ëì¤ì½ì´ ì ëì¬ë¥¼ ì¬ì©í ëª ìì ì¸ backing fieldë ì ë ê¸ì§
ì¢ì ì:
// C# 14ì field í¤ìë ì¬ì©
public string Name
{
get;
set => field = value ?? throw new ArgumentNullException(nameof(value));
}
// ì´ì© ì ìì´ ëª
ìì ì¸ backing fieldê° íìí ê²½ì°ìë ì¸ëì¤ì½ì´ ìì
private string name;
public string Name
{
get => name;
set => name = value ?? throw new ArgumentNullException(nameof(value));
}
ëì ì:
// ì¸ëì¤ì½ì´ ì ëì¬ë ì ë ê¸ì§
private string _name;
public string Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException(nameof(value));
}
Null-Conditional Assignment
?.를 ì¬ì©íì¬ null ì²´í¬ë¥¼ ê°ê²°íê² ê¸°ì í¨- ì¤ë³µëë null ì²´í¬ë¥¼ ì¤ì
ì¢ì ì:
customer?.Order = GetCurrentOrder();
ëì ì:
if (customer != null)
{
customer.Order = GetCurrentOrder();
}
Implicit Span Conversions
- ì±ë¥ ì¤ì ì½ëììë
Span<T>ìReadOnlySpan<T>를 íì©í¨ - ë°°ì´ê³¼ ì¤í¬ íì ê°ì ìë ë³íì ì´ì©í¨
ëª ëª ê·ì¹ (Naming Conventions)
Pascal Casing
- íì ì´ë¦ (class, record, struct, interface, enum)
- í¼ë¸ë¦ ë©¤ë² (íë¡í¼í°, ë©ìë, ì´ë²¤í¸)
- ë¤ìì¤íì´ì¤
ì¢ì ì:
public class CustomerOrder
{
public string OrderId { get; set; }
public void ProcessOrder() { }
}
Camel Casing
- ë¡ì»¬ ë³ì
- ë©ìë íë¼ë¯¸í°
- íë¼ì´ë¹ íë (ì¸ëì¤ì½ì´ ì ëì¬ë ì ë ì¬ì©íì§ ìì)
ì¢ì ì:
public class OrderProcessor
{
// ì¸ëì¤ì½ì´ ìì
private string customerName;
// ì¸ëì¤ì½ì´ ìì
private int orderCount;
public void ProcessOrder(string orderId)
{
var customerName = GetCustomerName(orderId);
string processedResult = Process(customerName);
}
}
ëì ì:
public class OrderProcessor
{
// ì¸ëì¤ì½ì´ ì ëì¬ë ì ë ê¸ì§
private string _customerName;
// ì¸ëì¤ì½ì´ ì ëì¬ë ì ë ê¸ì§
private int _orderCount;
}
Interface ëª ëª
- ì ëì¬
I를 ì¬ì©í¨
ì¢ì ì:
public interface IOrderProcessor
{
void Process(Order order);
}
íì íë¼ë¯¸í° ëª ëª
- ì ëì¬
T를 ì¬ì©í¨ - ì미 ìë ì´ë¦ì ë¶ì
ì¢ì ì:
public class Repository<TEntity> where TEntity : class
{
public void Add(TEntity entity) { }
}
ì½ë ë ì´ìì (Code Layout)
ë¤ì¬ì°ê¸°
- 공백(Space) 4ê°ë¥¼ ì¬ì©í¨
- í(Tab)ì ì¬ì©íì§ ìì
ì¤ê´í¸ (Curly Braces)
- Allman ì¤íì¼ (ìì ì¤ê´í¸ì ì¢ ë£ ì¤ê´í¸ë¥¼ ë³ëì íì ë°°ì¹)
- ì¤ê´í¸ ìëµì ì ë ê¸ì§ (1íì¼ë¡ 기ì í ì ìë ê²½ì°ìë ë°ëì ì¤ê´í¸ ì¬ì©)
ì¢ì ì:
public void ProcessOrder(Order order)
{
if (order != null)
{
order.Process();
}
}
// 1íì´ë¼ë ì¤ê´í¸ë¥¼ ì¬ì©í¨
if (isValid)
{
Execute();
}
for (int i = 0; i < 10; i++)
{
Process(i);
}
ëì ì:
// ì¤ê´í¸ ìëµ ê¸ì§
if (isValid)
Execute();
// ì¤ê´í¸ ìëµ ê¸ì§
for (int i = 0; i < 10; i++)
Process(i);
// ì¤ê´í¸ ìëµ ê¸ì§
if (order != null) order.Process();
í 기ì
- 1íì íëì statementë§ ê¸°ì í¨
- 1íì íëì ì ì¸ë§ 기ì í¨
- ë©ìë ì ìì íë¡í¼í° ì ì ì¬ì´ì ë¹ íì íë ë£ì
ì¢ì ì:
public class Order
{
public string OrderId { get; set; }
public void Process()
{
var result = Validate();
Execute(result);
}
private bool Validate()
{
return OrderId != null;
}
}
ë¤ìì¤íì´ì¤
- íì¼ ì¤ì½í ë¤ìì¤íì´ì¤(File-scoped namespace)를 ì¬ì©í¨
ì¢ì ì:
namespace YourProject.Orders;
public class OrderProcessor
{
// Implementation
}
ëì ì:
namespace YourProject.Orders
{
public class OrderProcessor
{
// Implementation
}
}
using ëë í°ë¸
- ë¤ìì¤íì´ì¤ ì ì¸ ë°ê¹¥ìª½ì ë°°ì¹í¨
- ìíë²³ ìì¼ë¡ ì ë ¬í¨
ì¢ì ì:
using System;
using System.Collections.Generic;
using System.Linq;
namespace YourProject.Orders;
íì ê³¼ ë³ì
íì ì§ì
- ì¸ì´ í¤ìë (
string,int,bool)를 ì¬ì©í¨ - ë°íì íì
(
System.String,System.Int32)ì ì¬ì©íì§ ìì
ì¢ì ì:
string name = "John";
int count = 10;
bool isValid = true;
ëì ì:
String name = "John";
Int32 count = 10;
Boolean isValid = true;
íì ì¶ë¡ (var)
- íì
ì´ í ë¹ëë ë´ì©ì¼ë¡ë¶í° ëª
ë°±í ê²½ì°ìë§
var를 ì¬ì©í¨ - 기본 ì ê³µ íì (Built-in type)ì ëª ìì ì¼ë¡ 기ì í¨
ì¢ì ì:
// ëª
ë°±í¨
var orders = new List<Order>();
// ëª
ë°±í¨
var customer = GetCustomer();
// 기본 íì
ì ëª
ì
int count = 10;
// 기본 íì
ì ëª
ì
string name = "John";
ëì ì:
// 기본 íì
ìì varë í¼í¨
var count = 10;
// 기본 íì
ìì varë í¼í¨
var name = "John";
문ìì´
문ìì´ ë³´ê° (String Interpolation)
- ì§§ì 문ìì´ ê²°í©ìë 문ìì´ ë³´ê°ì ì¬ì©í¨
ì¢ì ì:
string message = $"Order {orderId} processed successfully";
ëì ì:
string message = "Order " + orderId + " processed successfully";
StringBuilder
- 루í ë´ìì ëëì í
ì¤í¸ë¥¼ ì¶ê°íë ê²½ì°
StringBuilder를 ì¬ì©í¨
ì¢ì ì:
var builder = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
builder.Append($"Line {i}\n");
}
Raw String Literals
- ì´ì¤ì¼ì´í ìíì¤ë³´ë¤ Raw String Literals를 ì°ì í¨
ì¢ì ì:
string json = """
{
"name": "John",
"age": 30
}
""";
컬ë ì ê³¼ ê°ì²´ ì´ê¸°í
컬ë ì ì´ê¸°í
- C# 12 ì´íì Collection Expressions를 ì¬ì©í¨ (ìë¶ë¶ì “C# 12 ìµì 기륔 참조)
ê°ì²´ ì´ëì ë¼ì´ì (Object Initializer)
- ê°ì²´ ì´ëì ë¼ì´ì 를 ì¬ì©íì¬ ìì±ì ê°ìíí¨
ì¢ì ì:
var customer = new Customer
{
Name = "John",
Email = "john@example.com"
};
ìì¸ ì²ë¦¬ (Exception Handling)
구체ì ì¸ ìì¸ ìºì¹
- ì¼ë°ì ì¸
System.Exceptionëì 구체ì ì¸ ìì¸ë¥¼ ìºì¹í¨
ì¢ì ì:
try
{
ProcessOrder(order);
}
catch (ArgumentNullException ex)
{
Logger.Error("Order is null", ex);
}
ëì ì:
try
{
ProcessOrder(order);
}
catch (Exception ex) // ë무 ì¼ë°ì ì
{
Logger.Error("Error", ex);
}
using 문
- try-finally ëì
using문ì ì¬ì©í¨
ì¢ì ì:
using var connection = new SqlConnection(connectionString);
connection.Open();
// Process
ëì ì:
SqlConnection connection = null;
try
{
connection = new SqlConnection(connectionString);
connection.Open();
// Process
}
finally
{
connection?.Dispose();
}
LINQ
ì미 ìë ë³ìëª
- 쿼리 ë³ììë ì미 ìë ì´ë¦ì ì¬ì©í¨
ì¢ì ì:
var activeCustomers = from customer in customers
where customer.IsActive
select customer;
조기 íí°ë§
whereì ì ì¬ì©íì¬ ì¡°ê¸°ì ë°ì´í°ë¥¼ íí°ë§í¨
ì¢ì ì:
var result = customers
.Where(c => c.IsActive)
.Select(c => c.Name)
.ToList();
ììì íì ì§ì
- LINQ ì ì¸ììë ììì íì ì§ì ì ì¬ì©í¨
ì¢ì ì:
var query = from customer in customers
where customer.IsActive
select customer;
ëë¤ì (Lambda Expressions)
ì´ë²¤í¸ í¸ë¤ë¬
- ìì ê° íì ìë í¸ë¤ë¬ìë ëë¤ìì ì¬ì©í¨
ì¢ì ì:
button.Click += (s, e) => ProcessClick();
íë¼ë¯¸í° ììì´
- C# 14 기ë¥ì íì©íì¬ íì ì¶ë¡ ì ì ì§íë©´ì ììì´ë¥¼ ì¬ì©í¨
ì¢ì ì:
TryParse<int> parse = (text, out result) => int.TryParse(text, out result);
주ì (Comments)
ë¨ì¼ í 주ì
- ê°ê²°í ì¤ëª
ìë
//를 ì¬ì©í¨ - 주ì 구ë¶ì ë¤ì 공백ì íë ë£ì
- 주ìì ë°ëì ë¨ë íì 기ì í¨ (ì½ëì ê°ì íì 기ì ê¸ì§)
- 주ì ììë ë¹ íì íë ë£ì
ì¢ì ì:
// ê³ ê° ì£¼ë¬¸ì ì²ë¦¬í¨
ProcessOrder(order);
var processor = new OrderProcessor();
// 주문ì ì¤íí¨
var result = processor.ProcessOrder(order);
ëì ì:
ProcessOrder(order); // ê³ ê° ì£¼ë¬¸ì ì²ë¦¬í¨ (ì½ëì ê°ì íì ê¸ì§)
var processor = new OrderProcessor();
// ì´ íì ìì ë¹ íì´ ìì (ëì ì)
var result = processor.ProcessOrder(order);
XML 문ì
- í¼ë¸ë¦ 멤ë²ìë XML 문ì를 ì¬ì©í¨
ì¢ì ì:
/// <summary>
/// ì§ì ë 주문ì ì²ë¦¬í¨
/// </summary>
/// <param name="order">ì²ë¦¬í 주문</param>
/// <returns>ì²ë¦¬ ê²°ê³¼</returns>
public bool ProcessOrder(Order order)
{
// Implementation
}
ì ì ë©¤ë² (Static Members)
í´ëì¤ ì´ë¦ì ìí í¸ì¶
- ì ì 멤ë²ë í´ëì¤ ì´ë¦ì íµí´ í¸ì¶í¨
ì¢ì ì:
var result = OrderProcessor.ProcessOrder(order);
ëì ì:
var processor = new OrderProcessor();
// ì ì ë©ìë를 ì¸ì¤í´ì¤ë¥¼ íµí´ í¸ì¶íë ê²ì ì¤í´ì ìì§ê° ìì
var result = processor.ProcessOrder(order);
ì²´í¬ë¦¬ì¤í¸ (Checklist)
ì½ë ìì± ì
- .NET/C#ì ìµì ê¸°ë¥ (C# 12/13/14)ì íì íê³ ìì
- íë¡ì í¸ì íê² íë ììí¬ê° .NET 8 ì´íë¡ ì¤ì ëì´ ìì
- ëª ëª ê·ì¹ì ì´í´íê³ ìì
ì½ë ìì± ì¤
íì ê·ì¹:
- ì¸ëì¤ì½ì´ ì ëì¬ë¥¼ ì ë ì¬ì©íì§ ììì
- ì¤ê´í¸ë¥¼ ìëµíì§ ììì (1íì´ë¼ë ë°ëì ì¬ì©í¨)
- 주ìì ë°ëì ë¨ë íì 기ì íì (ì½ëì ê°ì íì 기ì íì§ ììì)
- 주ì ìì ë¹ íì íë ë£ìì
C# 12 ì´í 기ë¥:
- Primary Constructors를 ì¬ì©íê³ ìì (í´ë¹íë ê²½ì°)
- Collection Expressions를 ì¬ì©íê³ ìì
- Default Lambda Parameters를 íì©íê³ ìì (í´ë¹íë ê²½ì°)
- Alias Any Typeì¼ë¡ ë³µì¡í íì ì ë³ì¹ì ë¶ìì (í´ë¹íë ê²½ì°)
C# 13 ì´í 기ë¥:
- Params Collections를 ì¬ì©íê³ ìì (í´ë¹íë ê²½ì°)
- New Lock Typeì ì¬ì©íê³ ìì (ì¤ë ë ë기íê° íìí ê²½ì°)
- Partial Properties and Indexers를 íì©íê³ ìì (í´ë¹íë ê²½ì°)
- Implicit Index Access를 ê°ì²´ ì´ëì ë¼ì´ì ìì ì¬ì©íê³ ìì (í´ë¹íë ê²½ì°)
C# 14 ì´í 기ë¥:
-
fieldí¤ìë를 ì¬ì©íì¬ backing field를 ê°ê²°íê² ê¸°ì íì - Extension Members를 íì©íê³ ìì (í´ë¹íë ê²½ì°)
- Null-Conditional Assignment를 íì©íê³ ìì
- Lambda Parameters with Modifiers를 ì¬ì©íê³ ìì (í´ë¹íë ê²½ì°)
기본 ê·ì¹:
- íì¼ ì¤ì½í ë¤ìì¤íì´ì¤ë¥¼ ì¬ì©íê³ ìì
- ì¸ì´ í¤ìë (
string,int)를 ì¬ì©íê³ ìì -
var를 ì ì í ì¬ì©íê³ ìì (íì ì´ ëª ë°±í ê²½ì°ìë§) - 문ìì´ ë³´ê°ì ì¬ì©íê³ ìì
- Raw String Literals를 ì¬ì©íê³ ìì (í´ë¹íë ê²½ì°)
- Object Initializers를 ì¬ì©íê³ ìì
-
using문ì ì¬ì©íê³ ìì - 구체ì ì¸ ìì¸ë¥¼ ìºì¹íê³ ìì
- LINQ ììì 조기 íí°ë§ì ì¤ìíê³ ìì
- ì미 ìë ë³ìëª ì ì¬ì©íê³ ìì
- 주ìì´ ê°ê²°íê³ ëª íí¨
- í¼ë¸ë¦ 멤ë²ì XML 문ì를 기ì íì
- Allman ì¤íì¼ë¡ ì¤ê´í¸ë¥¼ ë°°ì¹íì
- ë¤ì¬ì°ê¸°ì 공백 4ê°ë¥¼ ì¬ì©íê³ ìì
ì½ë ìì± í
- ì½ëê° ì¼ê´ë ì¤íì¼ë¡ ìì±ëìì
- .NET 8 ì´íì ìµì ê¸°ë¥ (C# 12/13/14)ì íì©íê³ ìì
- ëª ëª ê·ì¹ì ë°ë¥´ê³ ìì
- ê°ë ì±ì´ ëê³ ì ì§ë³´ìí기 ì¬ì´ ì½ëì