Changing property type in class that implements in

2020-04-05 17:13发布

I'm writing a TemplateEngine that will allow me to use my own markup in text based files. I'm wanting to add controls as plugins as the application matures. Currently i've got a structure like the following:

interface IControl
    string Id
    object Value

class Label : IControl
    string Id
    string Value

class Repeater : IControl
    string Id
    List<IControl> Value

Now you'll see the strange part right away in the Repeater class with the Value property. I was hoping that having the Value type as object in the interface would allow me the flexibility to expand the controls as i go along. The compiler doesn't like this and for good reason i guess.

Bottom line: I'm trying to get all control classes to implement the same interface but have different types for the Value property.

Does anyone have any suggestions how to accomplish this?

Note: Please don't go into suggesting things like use Spark View Engine for templating. There is a reason i'm creating extra work for myself.

标签: c# interface
3条回答
Viruses.
2楼-- · 2020-04-05 17:29

you could also use generics:

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

I like this better than the explicit interface idea if it's just one return value that needs to change. However, I think if you had several properties that each would return a different type, you wouldn't want to have IControl. At least, I wouldn't. In that case I would recommend the explicit interfaces.

Of course, this wouldn't work if you didn't have access to the source of IControl.

Edit: had a typo. Fixed

查看更多
三岁会撩人
3楼-- · 2020-04-05 17:33

Normally the Repeater would implement something different, like an IItemsControl for example.

EDIT 1

(removed for brevity)

EDIT 2

Ah okay, you can always use explicit interface implementation of course:

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; }
    }
}
查看更多
\"骚年 ilove
4楼-- · 2020-04-05 17:42

No, the compiler doesn't allow same name fields to be of different data types other than what is defined in the interface in derived classes.

The properties (since no fields are allowed in interface) should be implemented in the deriving classes and they need to have same data type. So, you cannot probably do it with properties without explicit declaration.

However, if you make Value to be returned by a function, then it works, but you need to check the return type because the return types should match for the function, otherwise you will get error that interface's function was not implemented.

    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");

[Update]
You have to be careful if explicitly defining the body of the properties. Having one name for two properties would be dangerous if implementation is not done carefully.

These two properties if contain different definition, it would be unexplainable for the final use of the interface and classes.

        public IList<IControl> Value
        object IControl.Value

See this example:

    ...
    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;

You can observe that the list container in Repeater will have different values when data is added via IControl (because of the explicit definition of IContainer.Value).

查看更多
登录 后发表回答