从基本构造函数调用重写的函数
Calling an overridden function from a base constructor
我不应该从基本构造函数调用重写函数是有道理的,因为派生的 class 尚未构造。
但我想使用这种设计模式,其中每个派生 class 都提供了计算基础属性的方法 class 因为属性应该是不可变的并在构造函数中分配。
Shape.cs
public abstract class Shape
{
protected Shape()
{
Area = 0f;
Center = Vector2.Zero;
const int n = 36;
float du = 1/(float)n, dv = 1/(float)n;
for (int i = 0; i < n; i++)
{
float u = (i+0.5f)*du;
for (int j = 0; j < n; j++)
{
float v = (i+0.5f)*dv;
float f = GetAreaElement(u, v);
// Warning: Remove this call from a constructor to the overridable 'GetAreaElement' method.
Area += f*du*dv;
Center += GetPosition(u, v)*f*du*dv;
// Warning: Remove this call from a constructor to the overridable 'GetPosition' method.
}
}
Center /= Area;
}
public abstract Vector2 GetPosition(float u, float v);
protected abstract float GetAreaElement(float u, float v);
public float Area { get; }
public Vector2 Center { get; }
}
public class Circle : Shape
{
public Circle(float radius)
{
Radius=radius;
}
public float Radius { get; }
public override Vector2 GetPosition(float u, float v)
{
float r = u*Radius, θ = (float)(2*Math.PI)*v;
return new Vector2(
r*(float)Math.Cos(θ),
r*(float)Math.Sin(θ));
}
protected override float GetAreaElement(float u, float v)
{
return u*Radius;
}
}
public class Rectangle : Shape
{
public Rectangle(float side1, float side2)
{
Side1=side1;
Side2=side2;
}
public float Side1 { get; }
public float Side2 { get; }
public override Vector2 GetPosition(float u, float v)
{
return new Vector2((u-0.5f)*Side1, (v-0.5f)*Side2);
}
protected override float GetAreaElement(float u, float v)
{
return Side1*Side2;
}
}
那么这里的解决方案是什么?我想使用基础构造函数来定义属性,计算依赖于派生的实现class.
解决方法 1 - 未来计算器
一种解决方法是提供一个 protected
函数来计算属性,每个属性都会从派生 class 的每个构造函数中调用,但是 没有强制执行 这里。如果 class 忘记调用计算器函数,整个事情就会崩溃。属性现在是 private set
,这确实 不是一成不变的 。
public abstract class Shape
{
protected void Calculate()
{
...
float f = GetAreaElement(u, v);
...
Center += GetPosition(u, v)*f*du*dv;
...
}
public abstract Vector2 GetPosition(float u, float v);
protected abstract float GetAreaElement(float u, float v);
public float Area { get; private set; }
public Vector2 Center { get; private set; }
}
public class Circle : Shape
{
public Circle(float radius)
{
Radius=radius;
base.Calculate();
}
public float Radius { get; }
public override Vector2 GetPosition(float u, float v)
{
...
}
protected override float GetAreaElement(float u, float v)
{
...
}
}
解决方法 2 - 函数委托
另一种解决方法是将委托提供给所需的函数实现,作为基本 class 构造函数的参数。
public delegate float AreaFactor(float u, float v);
public delegate Vector2 PositionFunc(float u, float v);
public abstract class Shape
{
protected Shape(AreaFactor a, PositionFunc p)
{
this.GetAreaElement = a;
this.GetPosition = p;
...
float f = a(u, v);
this.Center += p(u, v)*f*du*dv;
...
}
public float Area { get; }
public Vector2 Center { get; }
public AreaFactor GetAreaElement { get; }
public PositionFunc GetPosition { get; }
}
public class Circle : Shape
{
public Circle(float radius)
: base(
(u, v) => u*radius,
(u,v)=>
{
float r = u*radius, θ = (float)(2*Math.PI)*v;
return new Vector2(
r*(float)Math.Cos(θ),
r*(float)Math.Sin(θ));
})
{
Radius=radius;
}
public float Radius { get; }
}
这对我来说似乎有点笨拙,而且我不确定我是否喜欢函数委托属性,而不是重写方法。
Question/Challege
能否[SO]提供一些其他方法来实现上述目标
- 基本属性是不可变的
- 基础属性是根据派生的 classes 的实现细节在构造函数中计算的。
- 每个派生 class 都拥有自己的不可变属性,用于描述派生 class。
一种选择是,不在构造函数中计算 Area
和 Center
,而是在 属性 getter 中延迟计算它们。它将需要一个支持字段来了解 属性 是否已计算,但它会消除警告。
你没有将中心传递给Shape
构造函数,这对我来说也很奇怪,但我不完全理解你的设计使用.
我的偏好是选择选项 2 - 将生成派生属性的方法传递给基本构造函数。它确实满足您的所有要求。如果美学是一个问题,也许使用 Func
可能会使代码更易于阅读。关键是 Func
是静态方法,这应该不是问题,因为它们计算对象的不可变属性。
void Main()
{
var square = new Square(5);
}
public abstract class Shape
{
protected Shape(Func<int> areaFunc)
{
Area = areaFunc();
}
public int Area { get; }
}
public class Square : Shape
{
public Square(int side): base( () => CalcArea(side) )
{
Side = side;
}
static int CalcArea(int side) => side * side;
public int Side { get; }
}
我不应该从基本构造函数调用重写函数是有道理的,因为派生的 class 尚未构造。
但我想使用这种设计模式,其中每个派生 class 都提供了计算基础属性的方法 class 因为属性应该是不可变的并在构造函数中分配。
Shape.cs
public abstract class Shape
{
protected Shape()
{
Area = 0f;
Center = Vector2.Zero;
const int n = 36;
float du = 1/(float)n, dv = 1/(float)n;
for (int i = 0; i < n; i++)
{
float u = (i+0.5f)*du;
for (int j = 0; j < n; j++)
{
float v = (i+0.5f)*dv;
float f = GetAreaElement(u, v);
// Warning: Remove this call from a constructor to the overridable 'GetAreaElement' method.
Area += f*du*dv;
Center += GetPosition(u, v)*f*du*dv;
// Warning: Remove this call from a constructor to the overridable 'GetPosition' method.
}
}
Center /= Area;
}
public abstract Vector2 GetPosition(float u, float v);
protected abstract float GetAreaElement(float u, float v);
public float Area { get; }
public Vector2 Center { get; }
}
public class Circle : Shape
{
public Circle(float radius)
{
Radius=radius;
}
public float Radius { get; }
public override Vector2 GetPosition(float u, float v)
{
float r = u*Radius, θ = (float)(2*Math.PI)*v;
return new Vector2(
r*(float)Math.Cos(θ),
r*(float)Math.Sin(θ));
}
protected override float GetAreaElement(float u, float v)
{
return u*Radius;
}
}
public class Rectangle : Shape
{
public Rectangle(float side1, float side2)
{
Side1=side1;
Side2=side2;
}
public float Side1 { get; }
public float Side2 { get; }
public override Vector2 GetPosition(float u, float v)
{
return new Vector2((u-0.5f)*Side1, (v-0.5f)*Side2);
}
protected override float GetAreaElement(float u, float v)
{
return Side1*Side2;
}
}
那么这里的解决方案是什么?我想使用基础构造函数来定义属性,计算依赖于派生的实现class.
解决方法 1 - 未来计算器
一种解决方法是提供一个 protected
函数来计算属性,每个属性都会从派生 class 的每个构造函数中调用,但是 没有强制执行 这里。如果 class 忘记调用计算器函数,整个事情就会崩溃。属性现在是 private set
,这确实 不是一成不变的 。
public abstract class Shape
{
protected void Calculate()
{
...
float f = GetAreaElement(u, v);
...
Center += GetPosition(u, v)*f*du*dv;
...
}
public abstract Vector2 GetPosition(float u, float v);
protected abstract float GetAreaElement(float u, float v);
public float Area { get; private set; }
public Vector2 Center { get; private set; }
}
public class Circle : Shape
{
public Circle(float radius)
{
Radius=radius;
base.Calculate();
}
public float Radius { get; }
public override Vector2 GetPosition(float u, float v)
{
...
}
protected override float GetAreaElement(float u, float v)
{
...
}
}
解决方法 2 - 函数委托
另一种解决方法是将委托提供给所需的函数实现,作为基本 class 构造函数的参数。
public delegate float AreaFactor(float u, float v);
public delegate Vector2 PositionFunc(float u, float v);
public abstract class Shape
{
protected Shape(AreaFactor a, PositionFunc p)
{
this.GetAreaElement = a;
this.GetPosition = p;
...
float f = a(u, v);
this.Center += p(u, v)*f*du*dv;
...
}
public float Area { get; }
public Vector2 Center { get; }
public AreaFactor GetAreaElement { get; }
public PositionFunc GetPosition { get; }
}
public class Circle : Shape
{
public Circle(float radius)
: base(
(u, v) => u*radius,
(u,v)=>
{
float r = u*radius, θ = (float)(2*Math.PI)*v;
return new Vector2(
r*(float)Math.Cos(θ),
r*(float)Math.Sin(θ));
})
{
Radius=radius;
}
public float Radius { get; }
}
这对我来说似乎有点笨拙,而且我不确定我是否喜欢函数委托属性,而不是重写方法。
Question/Challege
能否[SO]提供一些其他方法来实现上述目标
- 基本属性是不可变的
- 基础属性是根据派生的 classes 的实现细节在构造函数中计算的。
- 每个派生 class 都拥有自己的不可变属性,用于描述派生 class。
一种选择是,不在构造函数中计算 Area
和 Center
,而是在 属性 getter 中延迟计算它们。它将需要一个支持字段来了解 属性 是否已计算,但它会消除警告。
你没有将中心传递给Shape
构造函数,这对我来说也很奇怪,但我不完全理解你的设计使用.
我的偏好是选择选项 2 - 将生成派生属性的方法传递给基本构造函数。它确实满足您的所有要求。如果美学是一个问题,也许使用 Func
可能会使代码更易于阅读。关键是 Func
是静态方法,这应该不是问题,因为它们计算对象的不可变属性。
void Main()
{
var square = new Square(5);
}
public abstract class Shape
{
protected Shape(Func<int> areaFunc)
{
Area = areaFunc();
}
public int Area { get; }
}
public class Square : Shape
{
public Square(int side): base( () => CalcArea(side) )
{
Side = side;
}
static int CalcArea(int side) => side * side;
public int Side { get; }
}