C#返回类型重新协方差

时间:2013-11-22 12:27:46

标签: c# overloading covariance contravariance

我知道这已被覆盖,但我没有找到任何好的解决方案。在示例中,我使用两个返回类型类(ClassA和ClassB)和返回args类。我有一个我的ClassXml和ClassText扩展的基类,并希望阻止Print中的特定接口转换。这可能吗?

调用ClassXml和ClassText Execute方法,这些方法又调用调用适当print的基类功能。

   using System;

    namespace ConsoleApplication1
    {
        internal class Program
        {
            public class ClassA
            {
                public string Xml
                {
                    get { return "xml"; }
                }
            }

            public class ClassB
            {
                public char Text
                {
                    get { return 't'; }
                }
            }

            public interface IReturnArgs<out T>
            {
                string Name { get; set; }
                T Source { get; }
            }

            public class ReturnArgs<T> : IReturnArgs<T>
                where T : class
            {
                public string Name { get; set; }

                private T _source;

                public T Source
                {
                    get { return _source ?? (_source = (T) Activator.CreateInstance(typeof (T), new object[] {})); }
                }
            }

            private static void Main(string[] args)
            {
                var classA = new ReturnArgs<ClassA>();
                var classB = new ReturnArgs<ClassB>();

                var xml = new ClassXml();
                xml.Execute(classA);

                var text = new ClassText();
                text.Execute(classB);

                Console.ReadKey();
            }

            public abstract class ClassBase
            {
                public void Execute(IReturnArgs<object> args)
                {
                    // Do something common to both classes e.g. run tasks etc (not shown)
                    // calls print when completed, each call specific to child class.

                    Print(args);
                }

                /// <summary>
                /// Abstract print method. Print invokes the child implementation.
                /// </summary>
                /// <param name="args"></param>
                public virtual void Print(IReturnArgs<object> args){}
            }

            public class ClassXml : ClassBase
            {
                public void Execute(IReturnArgs<ClassA> args)
                {
                    //invoked externally (Main) calling base common functionality. 

                    base.Execute(args);

                    Print(args);
                }

                //print invoked by child class call - Note if invoked in this class then IReturnArgs<ClassA> could be used 
                //and a cast would not be necessary - however, this would separate common calls accross child classes.
                public void Print(IReturnArgs<ClassA> args)
                {
                    Console.WriteLine("ClassA Source {0}", ((IReturnArgs<ClassA>)args).Source.Xml);//ugly cast
                }
            }

            public class ClassText : ClassBase
            {
                public void Execute(IReturnArgs<ClassB> args)
                {
                    //invoked externally (Main) calling base common functionality. 

                    base.Execute(args);
                }

                //print invoked by base class call which requires the cast IReturnArgs<ClassB> from IReturnArgs<object>
                public override void Print(IReturnArgs<object> args)
                {
                    Console.WriteLine("ClassB Source {0}", ((IReturnArgs<ClassB>)args).Source.Text);//ugly cast
                }
            }
        }
    }

2 个答案:

答案 0 :(得分:0)

你似乎做了两次同样的事情。

ClassAClassBClassXMLClassText的关系过于强烈。

要解决此问题,您可以将ClassXML中的ClassAClassText中的ClassB的功能放在ToString()中。

或者,您可以使用ITextContent或其他接口(GetContent())和方法ClassContent来实现,分别返回XML和Text。这样您只需要一个{ {1}}只调用ITextContent.GetContent()

的类

完整的例子:

using System;

namespace ConsoleApplication1
{
    internal class Program
    {
        public Interface ITextContent
        {
           string GetContent();
        }

        public class ClassA : ITextContent
        {
            public string Xml
            {
                get { return "xml"; }
            }

            public string GetContent()
            {
                return this.Xml;
            }
        }

        public class ClassB : ITextContent
        {
            public char Text
            {
                get { return 't'; }
            }

            public string GetContent()
            {
                return this.Text;
            }
        }

        public interface IReturnArgs<out T>
        {
            string Name { get; set; }
            T Source { get; }
        }

        public class ReturnArgs<T> : IReturnArgs<T>
            where T : class
        {
            public string Name { get; set; }

            private T _source;

            public T Source
            {
                get { return _source ?? (_source = (T) Activator.CreateInstance(typeof (T), new object[] {})); }
            }
        }

        private static void Main(string[] args)
        {
            var classA = new ReturnArgs<ClassA>();
            var classB = new ReturnArgs<ClassB>();

            var xml = new ClassContent();
            xml.Execute(classA);

            var text = new ClassContent();
            text.Execute(classB);

            Console.ReadKey();
        }

        public class ClassContent
        {
            public void Execute(IReturnArgs<object> args)
            {
                Print(args);
            }

            public void Print(IReturnArgs<object> args)
            {
                Console.WriteLine(((IReturnArgs<ITextContent>)args).GetContent());
            }
        }
    }
}

答案 1 :(得分:0)

您真的不应该面对子类中的强制转换问题,因为基类不应该定义其参数类型依赖于子类的方法。基类应该是两者的通用接口,这意味着无论我分配给bargs,它都应该始终有效:

ClassBase b;
IReturnArgs<object> args;

...

b.Print(args);

但是,如果我使用ClassTextIReturnArgs<ClassA>,那么您的课程就会失败。

为什么不使用通用基础?

public abstract class ClassBase<T> {
    public void Execute(IReturnArgs<T> args) { ... }
    public virtual void Print(IReturnArgs<T> args) { ... }
}

public class ClassXml : ClassBase<ClassA> {
   public override void Print(IReturnArgs<ClassA> args) {
       Console.WriteLine("ClassA Source {0}", args.Source.Text);
   }
}