반응형
좋은 포인트예요. 오류 메시지는 null-coalescing assignment 연산자(??=) 가 C# 8.0에서 추가된 문법이라, C# 7.3 컴파일러로는 사용할 수 없다는 뜻입니다.
빠른 수정 (원인→해결)
- 원인:
- assemblies ??= new[] { Assembly.GetExecutingAssembly() }; // C# 8.0 문법
- C# 7.3 호환 수정:
- if (assemblies == null) assemblies = new[] { Assembly.GetExecutingAssembly() };
또한, 제가 드렸던 예시 중에 using var 도 C# 8.0 문법입니다. C# 7.3에서는 전통적인 using (...) { } 블록으로 바꿔주면 됩니다.
드롭인 패치: C# 7.3 호환 버전 메서드들
아래는 GuiLocalization의 문제되는 부분만 C# 7.3에 맞게 고쳐 쓴 코드입니다. (나머지 보조 메서드들은 그대로 사용하셔도 됩니다.)
1) LoadAuto(여러 어셈블리) – ??= 제거
public static void LoadAuto(string filePath, string languageCode, Assembly[] assemblies, string namespacePrefix = null)
{
if (assemblies == null)
assemblies = new[] { Assembly.GetExecutingAssembly() };
var all = new List<Type>();
foreach (var asm in assemblies)
{
all.AddRange(DiscoverContainers(asm, namespacePrefix));
}
LoadByTypes(filePath, languageCode, all);
}
2) LoadAuto(단일 어셈블리) – 동일
public static void LoadAuto(string filePath, string languageCode, Assembly assembly, string namespacePrefix = null)
{
if (assembly == null)
assembly = Assembly.GetExecutingAssembly();
var candidates = DiscoverContainers(assembly, namespacePrefix);
LoadByTypes(filePath, languageCode, candidates);
}
3) LoadByTypes – using var → 전통 using 블록
public static void LoadByTypes(string filePath, string languageCode, IEnumerable<Type> containerTypes,
int headerRowIndex = 0, int nameColumnIndex = 0)
{
EnsureCodePagesProvider(); // .NET Core/5+에서 .xls 지원
using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
var conf = new ExcelDataSetConfiguration
{
ConfigureDataTable = _ => new ExcelDataTableConfiguration { UseHeaderRow = false }
};
var ds = reader.AsDataSet(conf);
var sheetMap = new Dictionary<string, DataTable>(StringComparer.OrdinalIgnoreCase);
foreach (DataTable t in ds.Tables)
sheetMap[NormalizeSheetName(t.TableName)] = t;
foreach (var container in containerTypes)
{
var sheetName = container.Name;
var structName = "tag" + container.Name;
var staticFieldName = "g_st" + container.Name;
DataTable table;
if (!sheetMap.TryGetValue(sheetName, out table))
{
Debug.WriteLine("[WARN] Sheet '" + sheetName + "' not found. (skip)");
continue;
}
if (table.Rows.Count == 0)
{
Debug.WriteLine("[WARN] Sheet '" + sheetName + "' is empty. (skip)");
continue;
}
int langCol = FindLanguageColumnIndex(table, languageCode, headerRowIndex);
if (langCol < 0)
{
Debug.WriteLine("[WARN] Sheet '" + sheetName + "': language '" + languageCode + "' not found in header row. (skip)");
continue;
}
HashSet<string> dups;
var map = BuildNameValueMap(table, langCol, headerRowIndex, nameColumnIndex, out dups);
if (dups.Count > 0)
Debug.WriteLine("[WARN] Sheet '" + sheetName + "': duplicated keys: " + string.Join(", ", dups) + " (last wins)");
var structType = container.GetNestedType(structName, BindingFlags.Public | BindingFlags.NonPublic);
if (structType == null)
{
Debug.WriteLine("[ERROR] Nested struct '" + structName + "' not found in '" + container.FullName + "'.");
continue;
}
var staticField = container.GetField(staticFieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
if (staticField == null || staticField.FieldType != structType)
{
Debug.WriteLine("[ERROR] Static field '" + staticFieldName + "' not found or type mismatch in '" + container.FullName + "'.");
continue;
}
AuditKeys(structType, map, sheetName);
object boxed = Activator.CreateInstance(structType);
foreach (var f in structType.GetFields(BindingFlags.Public | BindingFlags.Instance))
{
if (f.FieldType != typeof(string)) continue;
string val;
if (map.TryGetValue(f.Name, out val))
f.SetValue(boxed, val);
}
staticField.SetValue(null, boxed);
Debug.WriteLine("[INFO] Applied: " + container.FullName + "." + staticFieldName + " from sheet '" + sheetName + "' (" + languageCode + ")");
}
}
}
그리고 BuildNameValueMap 시그니처를 C# 7.3 친화적으로 조금 바꿔줍니다(튜플 반환 대신 out 사용):
private static Dictionary<string, string> BuildNameValueMap(
DataTable table, int langCol, int headerRowIndex, int nameCol, out HashSet<string> duplicates)
{
var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
int startRow = headerRowIndex + 1;
for (int r = startRow; r < table.Rows.Count; r++)
{
var row = table.Rows[r];
string name = GetCellString(row, nameCol);
if (string.IsNullOrWhiteSpace(name)) continue;
string val = GetCellString(row, langCol) ?? string.Empty;
if (map.ContainsKey(name)) duplicates.Add(name);
map[name] = val;
}
return map;
}
참고: 식 본문 멤버(=>), 문자열 보간($"{x}") 등은 C# 7.3에서도 사용 가능합니다.
여기서는 최소한의 변경으로 ??=/using var/튜플 반환만 7.3 호환으로 바꿨습니다.
대안: C# 언어 버전 올리기 (가능하면 추천)
프로젝트가 허용된다면 **언어 버전을 8.0+**로 올리면, 원래 코드 그대로 쓰실 수 있습니다.
- SDK-style csproj:
- <PropertyGroup> <LangVersion>8.0</LangVersion> <!-- 또는 latest --> </PropertyGroup>
- Visual Studio: 프로젝트 속성 → Build → Advanced… → Language version
필요하시면 전체 파일(C# 7.3 대응본) 통으로 정리해서 드릴게요.
반응형
'C#' 카테고리의 다른 글
왜 IDisposable을 구현하나요? (0) | 2025.09.19 |
---|---|
“직접 델리게이트 콜백” → “.NET 표준 이벤트 패턴(Event + EventArgs)” (0) | 2025.09.19 |
sealed를 왜 써야 할까? (0) | 2025.09.19 |
C#의 readonly 키워드 (0) | 2025.09.19 |
VisionThread/EmguVision 라이브·검사 공통 파이프라인 리팩토링 (풀코드) (0) | 2025.09.10 |
EmguImage 안에 IInputArray를 반환하는 getter (0) | 2025.09.09 |
private static bool IsFiniteFloat(float v) => !(float.IsNaN(v) || float.IsInfinity(v)); (0) | 2025.09.09 |
소멸자(~EmguVision)로 GDI 리소스(Pen)를 해제하는 방식은 권장되지 않습니다. (0) | 2025.09.09 |