代码之家  ›  专栏  ›  技术社区  ›  Brian

在实现具有对象类型属性的接口的类中更改属性类型

  •  6
  • Brian  · 技术社区  · 16 年前

    我正在编写一个模板引擎,它允许我在基于文本的文件中使用自己的标记。随着应用程序的成熟,我想添加控件作为插件。目前我有如下结构:

    interface IControl
        string Id
        object Value
    
    class Label : IControl
        string Id
        string Value
    
    class Repeater : IControl
        string Id
        List<IControl> Value
    

    现在您将立即在repeater类中看到带有value属性的奇怪部分。我希望在接口中使用值类型作为对象将允许我在进行扩展时灵活地扩展控件。编译器不喜欢这样,我想是有原因的。

    一句话:我试图让所有的控件类实现相同的接口,但是值属性有不同的类型。

    有人对如何完成这项工作有什么建议吗?

    注意:请不要建议使用spark视图引擎作为模板。我为自己创造额外的工作是有原因的。

    3 回复  |  直到 8 年前
        1
  •  9
  •   herzmeister    16 年前

    通常情况下 Repeater 会实现一些不同的东西,比如 IItemsControl 例如。

    编辑1

    (为简洁起见删除)

    编辑2

    好吧,你当然可以使用显式的接口实现:

    interface IControl
    {
        string Id { get; set; }
        object Value { get; set; }
    }
    
    class Label : IControl
    {
        public string Id { get; set; }
        public string Value { get; set; }
    
        object IControl.Value
        {
            get { return this.Value; }
            set { this.Value = (string)value; }
        }
    }
    
    class Repeater : IControl
    {
        public string Id { get; set; }
        public IList<IControl> Value { get; set; }
    
        object IControl.Value
        {
            get { return this.Value; }
            set { this.Value = (IList<IControl>)value; }
        }
    }
    
        2
  •  3
  •   Joel    16 年前

    您还可以使用泛型:

    interface IControl<T> 
    {
        string ID{get;set;}
        T Value{get;set;}
    }
    
    class SomeControl : IControl<string>
    {
        public string ID{get;set}
        public string Value{get;set;}
    }
    
    class SomeOtherControl : IControl<int>
    {
        public string ID{get;set}
        public int Value{get;set;}
    }
    

    如果只是一个需要更改的返回值,我更喜欢这个方法,而不是显式的接口思想。但是,我认为如果您有几个属性,每个属性都将返回不同的类型,那么您就不希望有icontrol。至少,我不会。在这种情况下,我建议使用显式接口。

    当然,如果您不能访问icontrol的源代码,这就不起作用。

    编辑:输入错误。固定的

        3
  •  1
  •   Nayan    16 年前

    不,编译器不允许同一名称字段属于不同的数据类型,而不允许在派生类的接口中定义。

    属性(因为接口中不允许任何字段)应该在派生类中实现,并且它们需要具有相同的数据类型。因此,如果没有显式声明,您可能无法对属性执行此操作。

    但是,如果将值设置为由函数返回,则它可以工作,但需要检查返回类型,因为返回类型应与函数匹配,否则将出现错误,即接口的函数未实现。

        interface IControl
        {
            object Value();
        }
        class A : IControl
        {
            string m_value = string.Empty;
            public object Value() { return m_value; }
        };
        class B : IControl
        {
            List<IControl> m_value = new List<IControl>();
            public object Value() { return m_value; }
        };
        ....
        object o = new B().Value();
        if (o is List<IControl>)
            MessageBox.Show("List");
    

    [更新]
    如果显式定义属性的主体,则必须小心。如果不小心实现,两个属性只有一个名称将是危险的。

    这两个属性如果包含不同的定义,则在最终使用接口和类时将无法解释。

            public IList<IControl> Value
            object IControl.Value
    

    请参见以下示例:

        ...
        class Repeater : IControl
        {
            List<IControl> m_Value = new List<IControl>();
            public IList<IControl> Value
            {
                get { return this.m_Value; }
                set { this.m_Value = (IList<IControl>)value; }
            }
            object IControl.Value
            {
                get
                {
                    return this.m_Value;
                }
                set
                {
                    this.m_Value = new List<IControl>();
                    this.m_Value.Add(new Label());
                    this.m_Value.AddRange((List<IControl>)value);
                }
            }
        }
        ...
        Repeater b = new Repeater();
        IControl i = b;
        List<IControl> list = new List<IControl>();
        list.Add(new Repeater());
        i.Value = list;
    

    您可以看到,当通过icontrol添加数据时,repeater中的列表容器将具有不同的值(因为icontainer.value的显式定义)。