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();
}

我想要一个 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;
}

我建议使用类型系统来模拟此层次结构是错误的。您希望 ChildParentGrandParent 是分开的、独立的东西。这并不表示您通常在类型系统中期望的 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" })) { }
}