泛型类接口的Yield返回问题:IBasic接口类型处理困扰
解决IBasic接口实现类的CSV解析Yield返回问题
嘿,我明白你现在卡在这儿了——泛型CsvRepo<T>里要给IBasic的不同实现类(InputData1、InputData2、InputData3)用yield返回解析后的CSV记录,直接拆分字符串后不知道怎么转换成对应的具体类型对吧?我来给你分享几个实用的解决方案:
首先先把你的代码片段明确一下:
internal class CsvRepo<T> : ICsvRepo<T> where T : IBasic { private readonly ICsvSettings _settings; public CsvRepo(ICsvSettings settings) { _settings = settings; } public IEnumerable<T> GetRecords() { //return from line in File.ReadLines(_settings.Path) // select line.Split(...) 这里不知道怎么转换成对应的T类型 } }
方案一:给IBasic添加静态解析方法(推荐)
给IBasic接口定义一个静态抽象方法,让每个实现类自己负责解析逻辑,这样泛型类里就能统一调用:
首先修改IBasic接口:
public interface IBasic { // 你的其他接口成员 static abstract T ParseFromCsvLine<T>(string line) where T : IBasic, new(); }
然后让每个实现类实现这个静态方法,比如InputData1:
public class InputData1 : IBasic { public string PropA { get; set; } public int PropB { get; set; } // 实现自己的CSV行解析逻辑 public static InputData1 ParseFromCsvLine(string line) { var parts = line.Split(','); return new InputData1 { PropA = parts[0], PropB = int.Parse(parts[1]) }; } // 显式实现接口的泛型方法 static IBasic IBasic.ParseFromCsvLine<IBasic>(string line) => ParseFromCsvLine(line); }
接下来在GetRecords里就可以用yield轻松实现延迟加载了:
public IEnumerable<T> GetRecords() { foreach (var line in File.ReadLines(_settings.Path)) { if (string.IsNullOrWhiteSpace(line)) continue; // 跳过空行 var record = T.ParseFromCsvLine<T>(line); yield return record; } }
方案二:注入专属解析器(更符合依赖注入原则)
如果不想让实体类承担解析逻辑,可以定义一个解析器接口,为每个IBasic实现类写对应的解析器,然后注入到CsvRepo<T>中:
首先定义解析器接口:
public interface ICsvParser<T> where T : IBasic { T ParseLine(string line); }
然后为InputData1写解析器:
public class InputData1Parser : ICsvParser<InputData1> { public InputData1 ParseLine(string line) { var parts = line.Split(','); return new InputData1 { PropA = parts[0], PropB = int.Parse(parts[1]) }; } }
修改CsvRepo<T>的构造函数注入解析器:
internal class CsvRepo<T> : ICsvRepo<T> where T : IBasic { private readonly ICsvSettings _settings; private readonly ICsvParser<T> _parser; public CsvRepo(ICsvSettings settings, ICsvParser<T> parser) { _settings = settings; _parser = parser; } public IEnumerable<T> GetRecords() { foreach (var line in File.ReadLines(_settings.Path)) { if (string.IsNullOrWhiteSpace(line)) continue; yield return _parser.ParseLine(line); } } }
方案三:类型判断兜底(不推荐,仅当无法修改IBasic时使用)
如果不能修改IBasic接口,也不想加解析器,可以用类型判断来处理,但这种方式违反开闭原则,后续加新的IBasic实现类时需要修改这里的代码:
public IEnumerable<T> GetRecords() { foreach (var line in File.ReadLines(_settings.Path)) { if (string.IsNullOrWhiteSpace(line)) continue; var parts = line.Split(','); T record; if (typeof(T) == typeof(InputData1)) { record = (T)(object)new InputData1 { PropA = parts[0], PropB = int.Parse(parts[1]) }; } else if (typeof(T) == typeof(InputData2)) { // 对应InputData2的解析逻辑 record = (T)(object)new InputData2 { // 赋值你的属性 }; } else if (typeof(T) == typeof(InputData3)) { // InputData3的解析逻辑 record = (T)(object)new InputData3 { // 赋值你的属性 }; } else { throw new NotSupportedException($"不支持解析类型:{typeof(T)}"); } yield return record; } }
总结
优先选方案一或方案二,它们都遵循开闭原则,后续新增IBasic实现类时不需要修改CsvRepo<T>的代码,扩展性更好。方案三只是临时兜底的办法,尽量少用。
内容的提问来源于stack exchange,提问作者StudentOfLifeTMistakeTResult
相关产品推荐
相关产品推荐

