C# 使用 class 特定成员引用 child "base" 调用尊重
C# Using class specific member references that child "base" calls respect
我目前正在开发代码库,正在努力寻找最佳且干净的解决方案。我已经删除了问题的上下文以帮助将其简化为它的根组件。 Scale
属性 是对实际代码库中 class 更复杂状态的简化。我有一个关于如何解决这个问题的想法(我将在底部引用)——但是这个解决方案感觉很混乱,只是避开了我想更好地理解的区域。
Class 层次结构
public class GreatGrandparent
{
public virtual int Scale { get; set; } = 1;
public virtual int GetTrueScale()
{
return Scale;
}
}
public class Grandparent : GreatGrandparent
{
public override int Scale { get; set; } = 2;
public override int GetTrueScale()
{
return Scale * base.GetTrueScale();
}
}
public class Parent : Grandparent
{
public override int Scale { get; set; } = 8;
}
public class Child : Parent
{
public override int Scale { get; set; } = 4;
}
代码中的其他地方:
public class Main
{
Child aChild = new Child();
int aChildTrueScale = aChild.GetTrueScale();
}
- 预期结果:
4
(4×1)(参考编辑1)
- 实际结果:
16
(4×4)
- 想要的结果:
64
(4×8×2×1)
我想要一个 child 通过从它的 parent 中获取所有比例因子来找到它的相对比例,所以这会是:
child relative scale = child scale × parent scale × … × base class scale
我如何(如果可能)在 parent class 中定义一次 GetTrueScale
方法以获得所需的结果 - 所有 children 继承 - 到避免使用重复的实现连续覆盖该方法(GreatGrandparent 除外)。
“凌乱”解决方案
在每个 class 中定义一个单独的 property/field,并使用 ClassScale * base.GetTrueScale()
的 return 连续覆盖 aChildTrueScale()
方法,其中 ClassScale
在每个 Class.
上都是不同的 属性
编辑 1
预期结果是我当时基于我的理解的初步预期——认为在base
内调用Scale 引用将 尊重范围的变化 变化值以匹配 base
class 的值。通过一些进一步的测试,似乎无论 什么范围 当调用基方法时,引用的 Scale
值总是来自初始 objects scope(因此是 4*4)。
是否可以根据范围引用属性?所以在 base.GetTrueScale()
调用中,该函数调用中的任何引用都将在 base
范围内。还是我完全错过了 something/trying 来过度简化 children?
脚注
我对围绕数据科学的过程编程有一些经验,但是我对 object-oriented 编程还很缺乏经验,所以如果我对一些核心概念一无所知,请原谅我。我很乐意帮助澄清任何事情,感谢您花时间查看我的第一个问题! ^-^
(如果有人能想到更好的标题,请告诉我,我会解决它 - 一直在努力简单地定义问题)
类型层次结构将按 most base type -> most derived
的顺序调用。
由于您在 Parent
中没有重写方法,因此您的 Scale
不会相乘。这就是您获得 16
的原因。最好调试并查看代码的执行顺序。
您可以添加 class Parent
的覆盖 GetTrueScale()
方法以获得所需的值 64
。整个代码如下所示:
public class GreatGrandparent
{
public virtual int Scale { get; set; } = 1;
public virtual int GetTrueScale()
{
Console.WriteLine("GreatGrandparent: " + Scale);
return Scale;
}
}
public class Grandparent : GreatGrandparent
{
public override int Scale { get; set; } = 2;
public override int GetTrueScale()
{
Console.WriteLine("Grandparent: " + Scale);
return Scale * base.GetTrueScale();
}
}
public class Parent : Grandparent
{
public override int Scale { get; set; } = 8;
public override int GetTrueScale()
{
Console.WriteLine("Grandparent: " + Scale);
return Scale * base.GetTrueScale();
}
}
和Child
class:
public class Child : Parent
{
public override int Scale { get; set; } = 4;
}
我建议使用类型系统来模拟此层次结构是错误的。您希望 Child
、Parent
、GrandParent
是分开的、独立的东西。这并不表示您通常在类型系统中期望的 is-a
关系。
所以改为:
public class Thingy {
public int Scale {get;set;}
public Thingy Parent {get;set;}
public int GetTrueScale()
{
var current = this;
var accumulator = current.Scale;
current = current.Parent;
while(current!=null)
{
accumulator = accumulator * current.Scale;
current = current.Parent;
}
return accumulator;
}
}
然后创建您的每个对象:
var greatGrandParent = new Thingy {Scale = 1};
var grandParent = new Thingy {Scale = 2, Parent = greatGrandParent};
var parent = new Thingy {Scale = 8, Parent = grandParent};
var child = new Thingy { Scale = 4, Parent = parent};
您现在可以调用 child.GetTrueScale()
,层次结构的所有级别都会被考虑在内。
将 Children
集添加到 Thingy
和其他更有趣的行为留作练习。
Thingy
本身也可以是一个 IThingy
接口,如果不同级别确实需要不同的类型。
从评论中的讨论来看,事实证明你不需要你的 Scale
属性 是可设置的(它的值在构造时是固定的),你甚至不需要需要它是虚拟的。你可以在 GreatGrandfather
class 上只有一个 属性,像这样:
public class GreatGrandparent
{
public int Scale { get; }
public GreatGrandparent(int scale)
{
Scale = scale;
}
}
public class Grandparent : GreatGrandparent
{
public Grandparent(int scale) : base(scale * 2) { }
}
public class Parent : Grandparent
{
public Parent(int scale) : base(scale * 8) { }
}
public class Child : Parent
{
public Child(int scale) : base(scale * 4) { }
}
在您的实际代码中,您处理的是 HashSet
。您可以这样写,其中每个 child 都会向曾祖父母的 HashSet
:
添加新项目
public class GreatGrandparent
{
protected HashSet<string> hashSet = new();
public GreatGrandparent()
{
hashSet.Add("itemOne");
}
}
public class Grandparent : GreatGrandparent
{
public Grandparent()
{
hashSet.Add("itemTwo");
}
}
或者传递要添加到构造函数链中的项目(更昂贵,但也许更整洁?):
public class GreatGrandparent
{
protected HashSet<string> hashSet;
public GreatGrandparent(IEnumerable<string> items)
{
hashSet = new(items);
hashSet.Add("itemOne");
}
}
public class Grandparent : GreatGrandparent
{
public Grandparent(IEnumerable<string> items) : base(items.Concat(new[] { "itemTwo" })) { }
}
我目前正在开发代码库,正在努力寻找最佳且干净的解决方案。我已经删除了问题的上下文以帮助将其简化为它的根组件。 Scale
属性 是对实际代码库中 class 更复杂状态的简化。我有一个关于如何解决这个问题的想法(我将在底部引用)——但是这个解决方案感觉很混乱,只是避开了我想更好地理解的区域。
Class 层次结构
public class GreatGrandparent
{
public virtual int Scale { get; set; } = 1;
public virtual int GetTrueScale()
{
return Scale;
}
}
public class Grandparent : GreatGrandparent
{
public override int Scale { get; set; } = 2;
public override int GetTrueScale()
{
return Scale * base.GetTrueScale();
}
}
public class Parent : Grandparent
{
public override int Scale { get; set; } = 8;
}
public class Child : Parent
{
public override int Scale { get; set; } = 4;
}
代码中的其他地方:
public class Main
{
Child aChild = new Child();
int aChildTrueScale = aChild.GetTrueScale();
}
- 预期结果:
4
(4×1)(参考编辑1) - 实际结果:
16
(4×4) - 想要的结果:
64
(4×8×2×1)
我想要一个 child 通过从它的 parent 中获取所有比例因子来找到它的相对比例,所以这会是:
child relative scale = child scale × parent scale × … × base class scale
我如何(如果可能)在 parent class 中定义一次 GetTrueScale
方法以获得所需的结果 - 所有 children 继承 - 到避免使用重复的实现连续覆盖该方法(GreatGrandparent 除外)。
“凌乱”解决方案
在每个 class 中定义一个单独的 property/field,并使用 ClassScale * base.GetTrueScale()
的 return 连续覆盖 aChildTrueScale()
方法,其中 ClassScale
在每个 Class.
编辑 1
预期结果是我当时基于我的理解的初步预期——认为在base
内调用Scale 引用将 尊重范围的变化 变化值以匹配 base
class 的值。通过一些进一步的测试,似乎无论 什么范围 当调用基方法时,引用的 Scale
值总是来自初始 objects scope(因此是 4*4)。
是否可以根据范围引用属性?所以在 base.GetTrueScale()
调用中,该函数调用中的任何引用都将在 base
范围内。还是我完全错过了 something/trying 来过度简化 children?
脚注
我对围绕数据科学的过程编程有一些经验,但是我对 object-oriented 编程还很缺乏经验,所以如果我对一些核心概念一无所知,请原谅我。我很乐意帮助澄清任何事情,感谢您花时间查看我的第一个问题! ^-^
(如果有人能想到更好的标题,请告诉我,我会解决它 - 一直在努力简单地定义问题)
类型层次结构将按 most base type -> most derived
的顺序调用。
由于您在 Parent
中没有重写方法,因此您的 Scale
不会相乘。这就是您获得 16
的原因。最好调试并查看代码的执行顺序。
您可以添加 class Parent
的覆盖 GetTrueScale()
方法以获得所需的值 64
。整个代码如下所示:
public class GreatGrandparent
{
public virtual int Scale { get; set; } = 1;
public virtual int GetTrueScale()
{
Console.WriteLine("GreatGrandparent: " + Scale);
return Scale;
}
}
public class Grandparent : GreatGrandparent
{
public override int Scale { get; set; } = 2;
public override int GetTrueScale()
{
Console.WriteLine("Grandparent: " + Scale);
return Scale * base.GetTrueScale();
}
}
public class Parent : Grandparent
{
public override int Scale { get; set; } = 8;
public override int GetTrueScale()
{
Console.WriteLine("Grandparent: " + Scale);
return Scale * base.GetTrueScale();
}
}
和Child
class:
public class Child : Parent
{
public override int Scale { get; set; } = 4;
}
我建议使用类型系统来模拟此层次结构是错误的。您希望 Child
、Parent
、GrandParent
是分开的、独立的东西。这并不表示您通常在类型系统中期望的 is-a
关系。
所以改为:
public class Thingy {
public int Scale {get;set;}
public Thingy Parent {get;set;}
public int GetTrueScale()
{
var current = this;
var accumulator = current.Scale;
current = current.Parent;
while(current!=null)
{
accumulator = accumulator * current.Scale;
current = current.Parent;
}
return accumulator;
}
}
然后创建您的每个对象:
var greatGrandParent = new Thingy {Scale = 1};
var grandParent = new Thingy {Scale = 2, Parent = greatGrandParent};
var parent = new Thingy {Scale = 8, Parent = grandParent};
var child = new Thingy { Scale = 4, Parent = parent};
您现在可以调用 child.GetTrueScale()
,层次结构的所有级别都会被考虑在内。
将 Children
集添加到 Thingy
和其他更有趣的行为留作练习。
Thingy
本身也可以是一个 IThingy
接口,如果不同级别确实需要不同的类型。
从评论中的讨论来看,事实证明你不需要你的 Scale
属性 是可设置的(它的值在构造时是固定的),你甚至不需要需要它是虚拟的。你可以在 GreatGrandfather
class 上只有一个 属性,像这样:
public class GreatGrandparent
{
public int Scale { get; }
public GreatGrandparent(int scale)
{
Scale = scale;
}
}
public class Grandparent : GreatGrandparent
{
public Grandparent(int scale) : base(scale * 2) { }
}
public class Parent : Grandparent
{
public Parent(int scale) : base(scale * 8) { }
}
public class Child : Parent
{
public Child(int scale) : base(scale * 4) { }
}
在您的实际代码中,您处理的是 HashSet
。您可以这样写,其中每个 child 都会向曾祖父母的 HashSet
:
public class GreatGrandparent
{
protected HashSet<string> hashSet = new();
public GreatGrandparent()
{
hashSet.Add("itemOne");
}
}
public class Grandparent : GreatGrandparent
{
public Grandparent()
{
hashSet.Add("itemTwo");
}
}
或者传递要添加到构造函数链中的项目(更昂贵,但也许更整洁?):
public class GreatGrandparent
{
protected HashSet<string> hashSet;
public GreatGrandparent(IEnumerable<string> items)
{
hashSet = new(items);
hashSet.Add("itemOne");
}
}
public class Grandparent : GreatGrandparent
{
public Grandparent(IEnumerable<string> items) : base(items.Concat(new[] { "itemTwo" })) { }
}