使用泛型和“新”修饰符隐藏父级成员时出现 C# 继承问题

Posted

技术标签:

【中文标题】使用泛型和“新”修饰符隐藏父级成员时出现 C# 继承问题【英文标题】:Issue with C# inheritance when using Generics and 'new' modifier to hide a parent's member 【发布时间】:2019-01-17 03:50:28 【问题描述】:

我正在尝试创建具有 2 个级别的类层次结构 其中第二级覆盖其中一个属性(例如 Id)。

// Level 1
public class Level1 : IEntity

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id  get; set; 


// Level 2
public class Level2 : Level1

    // Override the 'Id' property in Level1
    // for the purpose of turning off auto-increment on Id.
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public new int Id  get; set; 

这是一个演示问题的自包含代码 sn-p。

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Example;

public class Program

    public static void Main()
    
        // Using concrete class, logic works correctly
        var tester = new Tester();

        Console.WriteLine("\n");

        // Using Generic, result is incorrect
        var testerGeneric = new TesterGeneric<Level2>();
    


///////////////////////////////////////////////////////////////////////////

public class Tester

    public Tester()
    
        Console.WriteLine("------ Tester Class ------");

        var listOfEntities = Level2.CreateDummyRecords();
        Console.WriteLine("List Count = " + listOfEntities.Count());        // Returns 6 (as expected)

        var groupedItems = listOfEntities.GroupBy(g => g.Id);
        Console.WriteLine("Grouped By Count = " + groupedItems.Count());    // Returns 3 (as expected)
    


public class TesterGeneric<TEntity>
                where TEntity : class, IEntity, new()

    public TesterGeneric()
    
        Console.WriteLine("------ TesterGeneric Class ------");

        var listOfEntities = (IEnumerable<TEntity>) Level2.CreateDummyRecords();
        Console.WriteLine("List Count = " + listOfEntities.Count());        // Returns 6 (as expected)

        var groupedItems = listOfEntities.GroupBy(g => g.Id);
        Console.WriteLine("Grouped By Count = " + groupedItems.Count());    // Returns 1 (should be 3)
    


///////////////////////////////////////////////////////////////////////////

namespace Example

    public interface IEntity
    
        int Id get; set;
    

    // Level 1
    public class Level1 : IEntity
    
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id  get; set; 
    

    // Level 2
    public class Level2 : Level1
    
        // Override the 'Id' property in Level1
        // for the purpose of turning off auto-increment on Id.
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public new int Id  get; set; 

        public static IEnumerable<Level2> CreateDummyRecords()
        
            var theList = new List<Level2>();

            theList.Add(new Level2()  Id=1 );

            theList.Add(new Level2()  Id=2 );
            theList.Add(new Level2()  Id=2 );

            theList.Add(new Level2()  Id=3 );
            theList.Add(new Level2()  Id=3 );
            theList.Add(new Level2()  Id=3 );

            return theList;
        
    

有两个“测试者”类

测试人员 TesterGeneric

Tester 使用具体类 Level2 并按 Id 属性正确分组记录。

即输出:

列表中有 6 项 3 组唯一 ID

TesterGeneric 通过 TEntity 泛型参数引用 Level2 并且不正确Id 属性对记录进行分组。

即输出:

列表中有 6 项 仅1组(组Key为0)

问题: 这是怎么发生的?

【问题讨论】:

使用new 遮蔽方法、属性或字段的情况很少见。这是邪恶的,应该避免。 【参考方案1】:

我认为我们对 new 和 override 有非常简单的概念。在这里你可以很容易地理解:

每当您覆盖子类属性/方法时,如果您将子对象转换为父类,那么您将拥有子类的功能。

每当您将 new 关键字与子类属性/方法一起使用时,如果您将子对象转换为父类,那么您将拥有父类功能。

这是给你的简单代码示例:

  class Parent

    public virtual int ID  get  return 40;  



class ChildWithNew: Parent

    public new int ID  get  return 50;  


class ChildWithOverride : Parent

    public override int ID  get  return 60;  


class Program

    static void Main(string[] args)
    


        Console.WriteLine("---Child with Override Kyword");
        ChildWithOverride cildWithOverride = new ChildWithOverride();

        Console.WriteLine(cildWithOverride.ID);
        Console.WriteLine(((Parent)cildWithOverride).ID);

        Console.WriteLine("---Child with New Kyword");
        ChildWithNew childWithNew = new ChildWithNew();

        Console.WriteLine(childWithNew.ID);
        Console.WriteLine(((Parent)childWithNew).ID);

        Console.ReadLine();
    



如果有帮助,请告诉我。

【讨论】:

谢谢@gaurav,它非常有用。问题解决了。我现在知道 Override 和 New 之间的区别了。【参考方案2】:

您没有覆盖属性 ID,现在您只是隐藏了您的属性。一旦您在 ID 中提供 virtual 和覆盖,问题就会得到解决。

【讨论】:

以上是关于使用泛型和“新”修饰符隐藏父级成员时出现 C# 继承问题的主要内容,如果未能解决你的问题,请参考以下文章

C# 使用new 关键字显式隐藏从基类继承的成员和内部类的使用

c#基础知识 类的继承 修饰符

C#笔记(十四)——接口泛型

new(C# 参考)

C# new用法总结

.NET(C#)new关键字的三种用法