使用泛型和 'new' 修饰符隐藏父成员时的 C# 继承问题
Issue with C# inheritance when using Generics and 'new' modifier to hide a parent's member
我正在尝试创建具有 2 个级别的 class 层次结构
其中第 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; }
}
这是演示问题的独立代码片段。
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;
}
}
}
有两个"tester"classes
- 测试人员
- TesterGeneric
Tester 使用具体的 class Level2 并按 Id 属性.
即输出:
- 列表中有 6 项
- 3组唯一ID
TesterGeneric 通过 TEntity 通用参数引用 Level2
不正确 按 Id 属性.
对记录进行分组
即输出:
- 列表中有 6 项
- 仅1组(且组Key为0)
问题:
这是怎么发生的?
您没有覆盖 属性 ID,现在您只是隐藏了您的 属性。一旦您在 ID 中提供虚拟和覆盖,问题就会得到解决。
我认为我们对 new 和 override 的概念非常简单。在这里你可以很容易地理解:
每当您重写子对象 class property/method 时,如果您将子对象转换为父对象,那么您将拥有子对象 class.
的功能
每当您将 new 关键字与子 class property/method 一起使用,然后如果您将子对象转换为父对象,那么您将拥有父对象 class 功能。
这里有一个简单的代码示例:
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();
}
}
如果有帮助请告诉我。
我正在尝试创建具有 2 个级别的 class 层次结构 其中第 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; }
}
这是演示问题的独立代码片段。
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;
}
}
}
有两个"tester"classes
- 测试人员
- TesterGeneric
Tester 使用具体的 class Level2 并按 Id 属性.
即输出:
- 列表中有 6 项
- 3组唯一ID
TesterGeneric 通过 TEntity 通用参数引用 Level2 不正确 按 Id 属性.
对记录进行分组即输出:
- 列表中有 6 项
- 仅1组(且组Key为0)
问题: 这是怎么发生的?
您没有覆盖 属性 ID,现在您只是隐藏了您的 属性。一旦您在 ID 中提供虚拟和覆盖,问题就会得到解决。
我认为我们对 new 和 override 的概念非常简单。在这里你可以很容易地理解:
每当您重写子对象 class property/method 时,如果您将子对象转换为父对象,那么您将拥有子对象 class.
的功能每当您将 new 关键字与子 class property/method 一起使用,然后如果您将子对象转换为父对象,那么您将拥有父对象 class 功能。
这里有一个简单的代码示例:
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();
}
}
如果有帮助请告诉我。