简体   繁体   中英

How can an object be accessed that inherits from a generic class?

This cracks my head, I hope somebody will have the wiliness to find a solution. Right below is a simplified code of the situation. The objective here is, that I wanth a separate, reusable library and to have the option of customizing these classes for each implementation. So i went for inheritance:

namespace Library {

   //a class that holds stuff together
   public class Core<THandler> where THandler:Handler
   {
      public THandler handler;
      public string text = "Something";
   }

   //class that react for extern events, say a keyboard events
   public class Handler 
   {
      protected object core;
      public Handler(object _core) {
         core = _core;
      }
      protected virtual void OnEvent() {
         //how to access 'core.text' ??
         var a = (Core<Handler>)core; //raises System.InvalidCastException 
                                      // since 'core' actualy contains 
                                      // an instance of MyCore
      }
   }
}
namespace MyImplementation {
   public class MyCore : Library.Core<MyHandler>
   {
   }

   public class MyHandler : Library.Handler
   {
      protected override void OnEvent() {
         base.OnEvent();
         //and what's the preferable option for accessing 'core' here?
         var a = (MyCore)core; //I use this
      }
   }
}

Since Handler.core contains an instance of MyCore, how can one access this object inside Hanlder.OnEventmethod?

The best approach here (in my mind, anyway) is to create an interface for the methods and properties that you need to access in Core .

For example, something similar to the following:

namespace Library {

    public interface ICore {
        string text {get; set;}
    }

   //a class that holds stuff together
    public class Core<THandler> : ICore where THandler : Handler
    {
        public THandler handler;
        private string m_Text = "Something";
        public string text
        {
            get
            {
                return m_Text;
            }
            set
            {
                m_Text = value;
            }
        }
    }

   //class that react for extern events, say a keyboard events
   public class Handler 
   {
      protected ICore core;
      public Handler(object _core) {
         core = (ICore)_core;
      }
      protected virtual void OnEvent() {
          Debug.WriteLine(core.text);
      }
   }
}

namespace MyImplementation {
   public class MyCore : Library.Core<MyHandler>
   {
   }

   public class MyHandler : Library.Handler
   {
      protected override void OnEvent() {
         base.OnEvent();
         //and what's the preferable option for accessing 'core' here?
         var a = (MyCore)core; //I use this
      }
   }
}

You are better off with using interfaces to bind Core and Handler to each other. You can still keep Core<T> generic. Generic type contraints wouldn't allow you to declare circular dependencies, which would otherwise be necessary to make your core work.

Note that the root cause for the problematic cast to be invalid is not that core instance is MyCore from your example, but because MyCore inherits from Core<MyHandler> and not Core<Handler> .

In case, for some reason, you'd insist of keeping the current design, you can make Handler generic as well, passing in its ancestor as type parameter, and fixing the cast like this:

public class Handler<THandler> where THandler : Handler<THandler>
{
    protected object core;
    public Handler(object _core) {
        core = _core;
    }
    public virtual void OnEvent() {
        // this works
        var a = (Core<THandler>)core;
    }
 }

So, to make it a bit more type-safe and avoid casts, the constructor should look like this:

    protected Core<THandler> core;
    public Handler(Core<THandler> _core) {
        core = _core;
    }

The MyHandler declaration would then, quite naturally, look like this:

public class MyHandler : Handler<MyHandler>
{
    …
}

The cast failes because Core is not covariant. Classes in general can not be covariant. Only interfaces and delegates can be covariant.

To reach the expected behaviour you can define the Core class as an interface with a out parameter, like below

public interface ICore<out T>where T:Handler
{
    T Handler {get;}
    string Text { get; }
}

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