C# is类型判断时能否为两个派生类使用同名模式变量
首先定义了基础接口IKlarfDefect:
public interface IKlarfDefect { int TEST { get; set; } int DEFECTID { get; set; } }
基于该接口派生两个实现类(注:原示例代码存在笔误,重复声明了IKlarfDefect接口,以下为修正后的结构):
public class RMTKlarfDefect : IKlarfDefect { public int TEST { get; set; } public int DEFECTID { get; set; } public List<RMTKlarfImageListInfo> KlarfImageList { get; set; } = new List<RMTKlarfImageListInfo>(); } public class CIMKlarfDefect : IKlarfDefect { public int TEST { get; set; } public int DEFECTID { get; set; } public List<CIMKlarfImageListInfo> KlarfImageList { get; set; } = new List<CIMKlarfImageListInfo>(); }
编写遍历IKlarfDefect集合的方法时,尝试在is类型判断中给两种派生类的匹配结果使用同一个变量名klarfDefectGeneric:
internal static string CreateDefectListString(IEnumerable<IKlarfDefect> klarfDefectList) { StringBuilder defectListString = new StringBuilder(); defectListString.AppendLine(""); foreach (var klarfDefect in klarfDefectList) { if ((klarfDefect is CIMKlarfDefect klarfDefectGeneric || klarfDefect is RMTKlarfDefect klarfDefectGeneric) ) { if (klarfDefectGeneric.KlarfImageList.Count == 0) { defectListString.Append("N;"); defectListString.AppendLine(); } } } }
编译时抛出如下错误:
Error CS0128 A local variable or function named 'klarfDefectGeneric' is already defined in this scope
C# 8.0及更早版本不允许在同一个||逻辑判断的不同is模式匹配分支中声明同名变量,两个klarfDefectGeneric变量同属一个方法作用域,编译器会判定为重复定义。
另外原示例代码除了接口重复定义的笔误外,还存在两个问题:方法签名要求返回string但代码中没有返回语句、两种实现类的KlarfImageList属性是不同泛型类型,没有在基础接口中声明,无法通过IKlarfDefect类型的变量直接访问。
方案1:升级C# 9.0+使用or模式匹配
C# 9.0开始新增类型联合的or模式,支持在同一模式判断中为不同类型的匹配结果声明同名变量,直接修改if判断条件即可:
if (klarfDefect is CIMKlarfDefect klarfDefectGeneric or RMTKlarfDefect klarfDefectGeneric) { if (klarfDefectGeneric.KlarfImageList.Count == 0) { defectListString.Append("N;"); defectListString.AppendLine(); } }
注意:由于两个实现类的KlarfImageList是不同泛型类型的集合,部分编译器配置下可能无法直接识别该公共属性,这种场景推荐使用方案2。
方案2:抽取公共成员到上层接口(推荐,兼容所有C#版本)
这是最符合面向对象设计原则的方案:把所有派生类共有的成员、判断逻辑抽到基础IKlarfDefect接口中,后续遍历不需要做类型判断,从根源避免类型转换问题:
// 基础接口定义所有公共成员 public interface IKlarfDefect { int TEST { get; set; } int DEFECTID { get; set; } // 暴露需要用到的判断逻辑,隐藏具体的列表实现 bool IsImageListEmpty { get; } } // RMT实现类 public class RMTKlarfDefect : IKlarfDefect { public int TEST { get; set; } public int DEFECTID { get; set; } public List<RMTKlarfImageListInfo> KlarfImageList { get; set; } = new List<RMTKlarfImageListInfo>(); public bool IsImageListEmpty => KlarfImageList.Count == 0; } // CIM实现类 public class CIMKlarfDefect : IKlarfDefect { public int TEST { get; set; } public int DEFECTID { get; set; } public List<CIMKlarfImageListInfo> KlarfImageList { get; set; } = new List<CIMKlarfImageListInfo>(); public bool IsImageListEmpty => KlarfImageList.Count == 0; }
修改后遍历方法可简化为:
internal static string CreateDefectListString(IEnumerable<IKlarfDefect> klarfDefectList) { StringBuilder defectListString = new StringBuilder(); defectListString.AppendLine(""); foreach (var klarfDefect in klarfDefectList) { if (klarfDefect.IsImageListEmpty) { defectListString.Append("N;"); defectListString.AppendLine(); } } // 补全缺失的返回逻辑 return defectListString.ToString(); }
方案3:低版本C#兼容写法
如果暂时不能升级C#版本、也不方便重构接口,可以分开做类型判断,提取公共逻辑避免同名变量冲突:
foreach (var klarfDefect in klarfDefectList) { bool isEmpty = false; if (klarfDefect is CIMKlarfDefect cimDefect) { isEmpty = cimDefect.KlarfImageList.Count == 0; } else if (klarfDefect is RMTKlarfDefect rmtDefect) { isEmpty = rmtDefect.KlarfImageList.Count == 0; } if (isEmpty) { defectListString.Append("N;"); defectListString.AppendLine(); } }
内容的提问来源于stack exchange,提问作者Datboydozy

