我有以下课程
class GridBase
{
public object DataSource { get; set; }
}
class GenericGrid<T> : GridBase
{
public new T DataSource { get; set; }
}
GridBase和Generic Grid类都可以实例化,也可以从任何一个下降。
这被认为是实现这种层次结构的正确/可接受的方式吗? 或者你应该加倍努力并像下面那样实施它
class GridBase
{
protected object dataSource;
public object DataSource { get { return dataSource; } set { dataSource = value; } }
}
class GenericGrid<T> : GridBase
{
public new T DataSource { get { return (T)dataSource; } set { dataSource = value; } }
}
当在后代中重新引入属性时,同样适用于非泛型类,我只是在这里使用一般示例。
另一个案例和问题
abstract class SomeBase
{
protected abstract void DoSomething();
}
class Child : SomeBase
{
protected override void DoSomething()
{
/* Some implementation here */
}
}
这里的情况是框架“X”声明SomeBase允许您定义自己的后代。他们创建的类(在运行时)然后从您的类下降(在本例中为Child)。但是,他们不会从DoSomething()的实现中调用您的DoSomething()方法。
就他们而言,他们不能盲目地调用base.Dosomething(),因为典型的情况是他们生成的类通常来自SomeBase,因为该方法是抽象的无效。 (就个人而言,我不喜欢C#中的这种行为。)
但无论如何,那是好的还是被接受的设计,那就是不调用base.xxx(),特别是当“意图”似乎相矛盾的时候?
编辑从框架设计的角度来看。这样做是否可以接受?如果不是,它将如何设计以防止这种情况或更好地传达其意图(在两种情况下)。
答案 0 :(得分:2)
对于DataSource
问题,我更喜欢以下模式
abstract class GridBase {
public abstract object DataSource { get; }
}
class GenericGrid<T> : GridBase {
private T m_data;
public override object DataSource {
get { return m_data; }
}
public T DataSourceTyped {
get { return m_data; }
set { m_data = value; }
}
}
原因
GridBase.DataSource
成员可写是类型不安全的。它允许我通过将值设置为GenericGrid<T>
实例non-T
的合同
new
的使用,因为它经常让用户感到困惑。我更喜欢后缀〜类型“为这个场景 编辑 OP已更正GridBase
和GenericGrid
均为可用类型
在这种情况下,我会说你需要重新考虑一下你的设计。将它们作为可用类型打开,可以很容易地暴露类型错误。
GenericGrid<int> grid = new GenericGrid<int>();
GridBase baseGrid = grid;
baseGrid.DataSource = "bad";
Console.Write(grid.DataSource); // Error!!!
如果以类似于我原始样本的方式将存储与值的访问分开,则设计将更加可靠。您可以使用以下代码进一步扩展它,以获得可用的非通用容器
class Grid : GridBase {
private objecm m_data;
public override object DataSource {
get { return m_data; }
}
public object DataSourceTyped {
get { return m_data; }
set { m_data = value; }
}
}
答案 1 :(得分:1)
我更喜欢这样的事情:
interface IGrid {
object DataSource { get; }
}
interface IGrid<T> {
T DataSource { get; }
}
public Grid : IGrid {
public object DataSource { get; private set; }
// details elided
}
public Grid<T> : IGrid<T> {
public T DataSource { get; private set; }
object IGrid.DataSource { get { return this.DataSource; } }
// details elided
}
请注意,我不是继承自Grid
。
答案 2 :(得分:1)
通用继承的第二种形式(强制转换基类'属性)更正确,因为它没有违反Liskov Substitution Principle。可以想象,泛型类的实例被强制转换为基类,并且通过基类访问Data指向不同的属性。为了使派生类成为基类的 substitutable ,您需要保持同步。
或者,您可以实现某种策略模式,其中基类从派生类请求Data属性,以避免笨拙的向下转换。这就是我的想法:
public class Base {
private readonly object m_Data; //immutable data, as per JaredPar suggestion that base class shouldn't be able to change it
publlic Base(object data) {
m_Data = data;
}
protected virtual object GetData() {return m_Data;}
public Object DataSource {get {return GetData();}}
}
public class Derived<T> : Base {
private T m_Data;
public Derived():base(null){}
protected override object GetData() {return m_Data;}
protected new T Data {return m_Data;}
}
关于第二个问题,我注意到我确实理解了这个问题。听起来像你遇到的问题是框架在运行时生成代理时不调用抽象方法,这在抽象类中总是合法的,因为执行代码的唯一方法是通过派生类必须覆盖抽象方法。