在派生类中存在新属性时,选择基类属性

时间:2014-04-19 09:14:08

标签: c# .net generic-collections nvelocity

我有一个Table类,它从其基类DbTableBase继承一个属性,并使用更具体的属性MegaList<DbColumnBase> Columns隐藏基类属性MegaList<TableColumn> Columns

最初我的应用程序仅针对Oracle,但最近重构为公共基础,因此我可以添加SQLServer和其他目标导致Velocity模板失败。我的NVelocity模板模块在任何具有由派生隐藏的基本实现的集合上失败,因为它评估了基本属性。

  context.Put("table", table);  // table is Table, not TableBase

  ...

  #foreach ($c in $table.Columns)
  $c.Name
  #end

我的容器声明是:

 public class MegaList<T> : ObservableCollection<T>
 {
     public MegaList() {} 
     // etc. etc.
 }

My Table类型有一个_columns字段,当它作为MegaList公开时,NVelocity无法迭代叶子Table.Columns列表:

public class Table : TableBase {

   MegaList<ColumnBase> _columns = new MegaList<ColumnBase>();

   public MegaList<ColumnBase> Columns   // template takes this one, even though it is hidden
   {
       get { return _columns; }
   }
}

public class Table : TableBase {

   new public MegaList<TableColumn> Columns   // Velocity ignores this, chooses base Columns
   {
      get { return _columns.DownCast<TableColumn>(); }
      set { _columns = value.UpCast<DbTableColumn, TableColumn>(); }
   }

   public MegaList<TableColumn> Columns2  // Velocity sees this 
   {
       get { return _columns;  }   
   }
 }

NVelocity正在评估基础TableBase.Columns,但如果我的模板引用$ table.Columns2,则NVelocity会评估Table.Columns2。

我无法将属性设置为虚拟,因为类型不同,虽然是一个更具体的思考,我认为这种行为是因为VelocityContext持有&#34;对象&#34;引用,并且在倍数的情况下选择正确的属性存在某种问题,但我认为它应该选择叶属性。

2 个答案:

答案 0 :(得分:1)

我无法重现您所看到的错误,如果有帮助,我已将下面的测试代码包含在内,但是......

如果您已捕获Visual Studio中打开的所有C​​LR异常,并且您在调试器下运行代码,那么您将看到NVelocity.Exception.ResourceNotFoundException的一个常见原因是。如果异常消息指出它无法找到资源VM_global_library.vm,则这是一个无害的例外,NVelocity正在尝试为全局宏加载可选的资源文件。由于NVelocity是Java Velocity的端口,因此异常通常用于Java库中的控制流。

[Test]
public void CustomObservableCollectionT()
{
    VelocityEngine velocityEngine = new VelocityEngine();
    velocityEngine.Init();

    Table table = new Table();
    table.Columns.Add(new TableColumn { Name = "Full Name" });
    table.Columns.Add(new TableColumn { Name = "Email Address" });
    table.Columns.Add(new TableColumn { Name = "DOB" });

    VelocityContext context = new VelocityContext();
    context.Put("table", table);

    using (StringWriter sw = new StringWriter())
    {
        velocityEngine.Evaluate(context, sw, "",
            "#foreach ($c in $table.Columns)\r\n" +
            "$c.Name\r\n" +
            "#end"
        );

        Assert.AreEqual(
            "Full Name\r\n" +
            "Email Address\r\n" +
            "DOB\r\n",
            sw.ToString());
    }
}
public class MegaList<T> : ObservableCollection<T>
{
}
public class Table
{
    private readonly MegaList<TableColumn> _columns = new MegaList<TableColumn>();

    public MegaList<TableColumn> Columns
    {
        get { return _columns; }
    }

    public List<TableColumn> ColumnList
    {
        get { return _columns.ToList(); }
    }

    public ObservableCollection<TableColumn> ColumnCollection
    {
        get { return new ObservableCollection<TableColumn>(_columns); }
    }
}
public class TableColumn
{
    public string Name { get; set; }
}

答案 1 :(得分:0)

我证明这是NVelocity中的错误/限制。我下载了NVelocity 1.1.0的源代码(Nuget的1.1.1版本),并链接到我的项目中并追溯到该问题。内省代码有点天真并且返回它找到的关于属性的第一件事,而不是检查是否有更好的候选者。使用.NET反射,可能存在具有相同名称但DeclaringType不同的类型的属性列表。特别是如果一个属性不是虚拟的,并且隐藏了一个基本属性,它必须检查propInfo.DeclaringType ==对象类型或者正确的OO是否在窗口之外。

我修补了NVelocity以正确使用C#继承的这一方面。我将通过代码库,看看还有什么可能需要解决这个问题。我希望在某个地方发布我的补丁版本,并希望改进文档。

对于NVelocity的文档有多么渺茫,以及工作或维护的证据很少,以及最新的源代码所在的位置,我感到困惑/困扰。我对这个项目以及其他项目缺乏运动感到非常沮丧,因为我有自己的商业产品,所以我可以为它自己分叉。