2013-11-22 130 views
1

我知道這已被覆蓋之前,但我還沒有找到任何好的解決方案。在這個例子中,我使用了兩個帶返回參數類的返回類型類(ClassA和ClassB)。我有一個基類,我的ClassXml和ClassText擴展並希望防止打印中的特定接口強制轉換。這可能嗎?C#返回類型重協方差

調用ClassXml和ClassText Execute方法,後者又調用調用相應打印的基類功能。

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 
       } 
      } 
     } 
    } 
+3

爲什麼不使用通用接口/基類? –

+0

在該示例中有一個名爲ClassBase的基類。我想要理解或解決的是如何在子類/父類之間傳遞ReturnArgs,讓它返回Source屬性的不同返回類型。 – Craig

回答

0

你似乎做了兩次相同的事情。

ClassAClassBClassXMLClassText的關聯性太強。

要解決此問題,您可以將ClassXML的功能放入ClassAClassTextClassB

或者,你也可以實現類似ToString()或其他接口(ITextContent與方法GetContent())分別返回XML和文本。你只會有隻需要一個ClassContent類只是調用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()); 
      } 
     } 
    } 
} 
+0

感謝您的帖子。 ClassA和ClassB是爲了說明不同類型的T.我試圖在基類中使用IReturnArgs 或者不在子級別投射時調用子類(ClassXml,ClassText)中的執行。 – Craig

+0

我的答案依然如此。通過爲ClassA和ClassB引入一個通用接口,讓您獲得所需的內容,您可以在子級別刪除該施放。 – Davio

+0

感謝您的回答。我現在已經接受了這個答案。 – Craig

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); 
    } 
} 
+0

感謝您的代碼。是的,這將工作,但不幸的是需要我的基類是通用的。 – Craig

+0

@ user3021646有沒有理由不能/不應該是通用的? – nmclean