Why aren't generic type constraints inheritable/hierarchically enforced

后端 未结 4 639
故里飘歌
故里飘歌 2020-11-29 06:15

Item class

public class Item
{
    public bool Check(int value) { ... }
}

Base abstract class with generic type constraint

         


        
4条回答
  •  悲&欢浪女
    2020-11-29 06:33

    Below is a scenario where the implicit nature of this behavior causes different behavior than expected:

    I recognize that this scenario may seem extravagant in the amount of setup, but this is just one example of where this behavior might cause a problem. Software applications can be complicated, so even though this scenario may seem complicated, I wouldn't say that this can't happen.

    In this example there is an Operator class that implements two similar interfaces: IMonitor and IProcessor. Both have a start method and an IsStarted property, but the behavior for each interface within the Operator class is separate. I.e. there is a _MonitorStarted variable and a _ProcessorStarted variable within the Operator class.

    MyClass derives from ClassBase. ClassBase has a type constraint on T that it must implement the IProcessor interface, and according to the suggested behavior MyClass inherits that type constraint.

    MyClass has a Check method, which is built with the assumption that it can get the value of the IProcessor.IsStarted property from the inner IProcessor object.

    Suppose someone changes the implementation of ClassBase to remove the type constraint of IProcessor on the generic parameter T and replace it with a type contraint of IMonitor. This code will silently work, but will produce different behavior. The reason is because the Check method in MyClass is now calling the IMonitor.IsStarted property instead of the IProcessor.IsStarted property, even though the code for MyClass hasn't changed at all.

    public interface IMonitor
    {
        void Start();
    
        bool IsStarted { get; }
    }
    
    public interface IProcessor
    {
        void Start();
    
        bool IsStarted { get; }
    }
    
    public class Operator : IMonitor, IProcessor
    {
        #region IMonitor Members
    
        bool _MonitorStarted;
    
        void IMonitor.Start()
        {
            Console.WriteLine("IMonitor.Start");
            _MonitorStarted = true;
        }
    
        bool IMonitor.IsStarted
        {
            get { return _MonitorStarted; }
        }
    
        #endregion
    
        #region IProcessor Members
    
        bool _ProcessorStarted;
    
        void IProcessor.Start()
        {
            Console.WriteLine("IProcessor.Start");
            _ProcessorStarted = true;
        }
    
        bool IProcessor.IsStarted
        {
            get { return _ProcessorStarted; }
        }
    
        #endregion
    }
    
    public class ClassBase
        where T : IProcessor
    {
        protected T Inner { get; private set; }
    
        public ClassBase(T inner)
        {
            this.Inner = inner;
        }
    
        public void Start()
        {
            this.Inner.Start();
        }
    }
    
    public class MyClass : ClassBase
        //where T : IProcessor
    {
        public MyClass(T inner) : base(inner) { }
    
        public bool Check()
        {
            // this code was written assuming that it is calling IProcessor.IsStarted
            return this.Inner.IsStarted;
        }
    }
    
    public static class Extensions
    {
        public static void StartMonitoring(this IMonitor monitor)
        {
            monitor.Start();
        }
    
        public static void StartProcessing(this IProcessor processor)
        {
            processor.Start();
        }
    
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var @operator = new Operator();
    
            @operator.StartMonitoring();
    
            var myClass = new MyClass(@operator);
    
            var result = myClass.Check();
    
            // the value of result will be false if the type constraint on T in ClassBase is where T : IProcessor
            // the value of result will be true if the type constraint on T in ClassBase is where T : IMonitor
        }
    }
    

提交回复
热议问题