简体   繁体   中英

Generic class Inheritance issue

I have the following situation:

class MyControl<T> : UserControl where T:TClass
{
    public T Field {}
    public event EventHandler<MyEventArgs<T>> MyEvent;
}
class DerControl1 : MyControl<ClassA> {}
class DerControl2 : MyControl<ClassB> {}

How I should implement inheritance to have a base class for DerControl1, DerControl2 to have access to interface of MyControl?

SomeBaseClass control = condition ? DerControl1 :DerControl2;
control.Field = null;
control.Enabled=false;

What class should SomeBaseClass be?

Define another, non-generic interface IMyControl and implement it explicitly:

public interface IMyControl
{
    public TClass Field { get; set; }
    public bool Enabled { get; set; }
}

class MyControl<T> : UserControl, IMyControl where T:TClass
{
    public T Field { get; set; }
    TClass IMyControl.Field
    {
        get { return this.Field; }
        set { this.Field = (T)value; }
    }
    public event EventHandler<MyEventArgs<T>> MyEvent;
}

You can now cast your derived controls to IMyControl and access the Field property as a type TClass . Trying to return as anything else violates co/contravariance (you can return an object but it is preferable in this case to return a TClass since the type constraint already exists)

You can use UserControl to access the Enabled field, but there is no common type that defines Field . You cannot put Field in a common non-generic interface, because its type is generic.

Starting with C# 4.0, you can use dynamic for things like that:

dynamic control = condition ? DerControl1 :DerControl2;
control.Field = null;
control.Enabled=false;

This may be slightly slower, but it will compile and do what you want.

The use case is unclear. If this code:

control.Field = null;
control.Enabled=false;

won't be placed into a generic method (or into a method of a generic type), than you don't need generic at all:

class MyControl : UserControl where T:TClass
{
    public object Field {}
    public event EventHandler<MyEventArgs> MyEvent;
}

Otherwise, this code will work just fine.

Using your own question, couldn't you just use an interface?

public interface IMyControl<T> where T:class
{
    T Field { get; set;}
    event EventHandler<MyEventArgs<T>> MyEvent;
}

public class MyControl<T> : UserControl, IMyControl<T> where T:class
{
    public T Field { get; set;}
    public event EventHandler<MyEventArgs<T>> MyEvent;       
}

And then to use :

// (which equates to IMyControl control = ...)
var control = condition ? new MyControl<ClassA>() : new MyControl<ClassB>();
control.Field = null;
control.Enabled = false;

This saves you having multiple classes for each Type parameter you want to use and therefore encourages code reuse and follows polymorphism (OOP).

Hope that is helpful!

EDIT

OK - so Alex was right and I shouldn't have fired from the hip on that one! After a bit of investigation this seems to be a lot harder than I thought! So I began to wonder if what you're asking is the right way to go. Now, whether it is the sort of answer you want or not, I don't know, but here it is for you to ponder.

Basically, donn't use generics. It makes life really really difficult in this scenario! Instead, hide behind interfaces. The code below achieves the same as you're trying to above, but with the exception that you now have field tied to a 'concrete' interface. But realistically, you want this - you should have known behaviours and patterns, otherwise, to my mind, you don't know what your system should be doing and therefore there's something wrong with your requirements. Anyway, surmon over, here's some code!

public interface IMyClass
{
    int I { get; set; }
    string S { get; set; }
}

public interface IMyControl
{
    IMyClass Field { get; set; }
    event EventHandler<EventArgs> MyEvent;
}

public class MyControl : UserControl, IMyControl
{
    public MyControl(IMyClass field)
    {
        this.Field = field;
    }

    public IMyClass Field { get; set; }

    public event EventHandler<EventArgs> MyEvent;
}

public class MyClass1 : IMyClass
{
    public int I { get; set; }
    public string S { get; set; }

    public override string ToString()
    {
        return "I am a MyClass1!!!";
    }
}

public class MyClass2 : IMyClass
{
    public int I { get; set; }
    public string S { get; set; }

    public override string ToString()
    {
        return "I am a MyClass2!!!";
    }
}

var control = someCondition ? new MyControl(new MyClass1()) : new MyControl(new MyClass2());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM