2010-12-01 109 views
5

由於泛型問題,我遇到了一些嚴重的設計問題。也許有人有一些建議。通用類型轉換

編輯:所以,我知道這通常不會完成,但我完全改變了我的示例代碼,因爲我意識到原始的僞代碼並沒有真正解釋我的問題。下面的代碼更接近我正在處理的真實例子。我希望我的問題會更清楚。我向前道歉,它有點冗長,但從我的經驗來看,當你試圖建立一個更復雜的結構時,泛型的問題通常會出現。所以:

class Program 
    { 
     static void Main(string[] args) 
     { 
      IConnector<IService> connector = ConnectorBuilderFactory.NewBuilder<IService>("someEndpoint").MakeReliable().GetConnector(); 
      connector.Connect(); 
     } 
    } 

    public interface IService : IConnectionMaintainable 
    { 
     void DoSomething(); 
    } 

    public interface IConnectionMaintainable 
    { 
     DateTime GetServerTime(); 
    } 

    public interface IConnector<T> 
    { 
     T Channel { get; } 
     void Connect(); 
     void Disconnect(); 
    } 

    public interface IConnectorBuilder<T> 
    { 
     IConnector<T> GetConnector(); 
     IConnectorBuilder<T> MakeReliable(); 
     // ...more connector-configuration methods 
    } 

    public class ChannelWatchDog<T> where T : IConnectionMaintainable 
    { 
     private IConnector<T> connector; 

     public ChannelWatchDog(IConnector<T> connector /*various other parameters*/) 
     { 
      this.connector = connector; 
     } 

     // ...methods that use connector's Connect, Disconnect, and GetServerTime methods 
    } 

    public class Connector<T> : IConnector<T> 
    { 
     private T channel; 

     public Connector(string endpoint) 
     { 
      // ...build channel 
     } 

     public T Channel 
     { 
      get { return channel; } 
     } 

     public void Connect() 
     { 
      // ...connect to server 
     } 

     public void Disconnect() 
     { 
      // ...disconnect from server 
     } 
    } 

    public class ConnectorBuilder<T> : IConnectorBuilder<T> 
    { 
     private string endpoint; 

     public ConnectorBuilder(string endpoint) 
     { 
      this.endpoint = endpoint; 
     } 

     public IConnector<T> GetConnector() 
     { 
      Connector<T> connector = new Connector<T>(endpoint); 

      // If reliability was requested, build the ChannelWatchDog: Following line does not compile: 
      // ChannelWatchDog<T> watchDog = new ChannelWatchDog<T>(connector); 

      return connector; 
     } 

     public IConnectorBuilder<T> MakeReliable() 
     { 
      // save various parameters required to build the ChannelWatchDog 
      return this; 
     } 
    } 

    public static class ConnectorBuilderFactory 
    { 
     public static IConnectorBuilder<T> NewBuilder<T>(string endpoint) 
     { 
      return new ConnectorBuilder<T>(endpoint); 
     } 
    } 

所以,第一,如果你發現在ConnectorBuilder類的GetConnector方法,你會看到代碼的註釋行,這不,如果取消註釋編譯。這條線是我的問題的本質。這個問題可能是明顯的代碼,但我會盡量嘗試解釋的情況下,它是不是:

  1. 我有一個內部類(ChannelWatchDog),需要的IConnector。但不僅僅是IConnector,IConnector,因爲除非非通用IConnector方法外,它還需要IConnectionMaintainable接口中的GetServerTime方法。

  2. 爲了簡化連接器的構建,我希望使用表達式構建器模式(IConnectionBuilder接口)來實現構建器。但是,我希望能夠構建任何IConnector,而不僅僅是IConnector可持續發展>。因此,我不能像我爲ChannelWatchDog限制它一樣限制IConnectorBuilder中的T.由於缺少這個約束,當GetConnector被調用時,我無法構建它。將約束添加到MakeReliable方法中並沒有幫助。

所以,基本上我發佈這個問題的原因是我想做一些顯然不可能的事情。我想讓ChannelWatchDog和ConnectorBuilder類看起來像這樣:

public class ChannelWatchDog 
    { 
     private IConnector<IConnectionMaintainable> connector; 

     public ChannelWatchDog(IConnector<IConnectionMaintainable> connector /*various other parameters*/) 
     { 
      this.connector = connector; 
     } 

     // ...methods that use connector's Connect, Disconnect, and GetServerTime methods 
    } 

    public class ConnectorBuilder<T> : IConnectorBuilder<T> 
    { 
     private string endpoint; 

     public ConnectorBuilder(string endpoint) 
     { 
      this.endpoint = endpoint; 
     } 

     public IConnector<T> GetConnector() 
     { 
      Connector<T> connector = new Connector<T>(endpoint); 

      // If reliability was requested, build the ChannelWatchDog: Following line does not compile: 
      ChannelWatchDog watchDog = new ChannelWatchDog((IConnector<IConnectionMaintainable>)connector); 

      return connector; 
     } 

     public IConnectorBuilder<TReliable> MakeReliable<TReliable>() where TReliable : T, IConnectionMaintainable 
     { 
      // save various parameters required to build the ChannelWatchDog 
      return (IConnectorBuilder<TReliable>)this; 
     } 
    } 

但是,對IConnector的轉換在運行時失敗。

所以這比我原本想要的要長得多。如果你已經閱讀了這麼多,那麼你已經得到了我的謝意:) 歡迎任何想法,包括重構代碼。

順便說一句,自己沒有找到解決方案,我在工廠中創建了不同的ConnectorBuilder(在這種情況下,一個ReliableConnectorBuilder)和不同的工廠方法。但我不太喜歡這個解決方案。

編輯:只是爲了澄清和重申:我不能約束IConnector或ConnectionBuilder,因爲這些需要支持IConnectionMaintainable接口未實現的情況。

+0

什麼是不編譯的行上的編譯錯誤和......哪一行是無法在運行時強制轉換?你有兩個不同的問題(有效的2個不同的問題)張貼在這裏。哪個是哪個?你也可以嘗試用真正的問題而不僅僅是陳述來解決問題嗎? – Maslow 2010-12-14 14:30:13

+0

@Maslow,註釋行不編譯,因爲連接器不受ChannelWatchDog的限制。我希望爲此解決方案,這是鑄造連接器<T>連接器<IConnectionMaintainable>是在運行時失敗。也就是說,第一個示例中沒有編譯的行與第二個示例中修改後的行在運行時引發異常的行相同。不,這不是兩個單獨的問題。這是一個問題,我正在提供我在運行時失敗的嘗試解決方案之一。 – joniba 2010-12-15 14:09:23

回答

5

接口代碼?

GenericClass<IFoo> wrapper = new GenericClass<IFoo>(new FooImplementor()); 
Acceptor acceptor = new Acceptor(wrapper); 
+0

是的,這是顯而易見的解決方案。這只是我沒有解釋清楚自己:)只要我有空,我會更新我的問題,因爲它比聽起來更復雜。 – joniba 2010-12-02 09:02:06

+0

抱歉,延遲。我已經重寫了這個問題。我知道這是相當長的,但會很感激任何輸入。 – joniba 2010-12-05 14:55:21

0

你可以有GenericClass<T>執行/擴展非泛型接口/基類,很像是在.NET列表和可枚舉的情況。實際泛型類型可以是返回Type對象的抽象方法/屬性,並且可以在泛型類中實現。

1

你想爲你的GenericClass所需的東西叫做Covarianz。有關更多詳情,請參閱this。我對子級去馬克的回答,但如果這是不是你想要的,那就試試這個:

class GenericClass<out T> { ... } 
+0

非常有趣。但我正在使用.net 3.5,無法升級。 – joniba 2010-12-02 09:02:51

0

如果此行未編譯的主要問題是:

// If reliability was requested, build the ChannelWatchDog: Following line does not compile: 
     ChannelWatchDog watchDog = new ChannelWatchDog((IConnector<IConnectionMaintainable>)connector); 

那麼我認爲這應該是

ChannelWatchDog watchDog = new ChannelWatchDog(connector); 

除了運行時轉換失敗,我相信因爲IConnector需要被約束:

public interface IConnector<T> where T:IConnectionMaintainable 
{ 
    T Channel { get; } 
    void Connect(); 
    void Disconnect(); 
} 

這是從臀部,並希望提供見解或提示。約束根據linqpad進行編譯。

而且它導致層疊約束要求,所以這裏的代碼的其餘部分,它編譯:

void Main() 
{ 

} 

// Define other methods and classes here 
    public interface IConnectionMaintainable 
{ 
    DateTime GetServerTime(); 
} 

public interface IConnector<T> where T:IConnectionMaintainable 
{ 
    T Channel { get; } 
    void Connect(); 
    void Disconnect(); 
} 

public interface IConnectorBuilder<T> where T:IConnectionMaintainable 
{ 
    IConnector<T> GetConnector(); 
    IConnectorBuilder<T> MakeReliable(); 
    // ...more connector-configuration methods 
} 
public class ChannelWatchDog 
{ 
    private IConnector<IConnectionMaintainable> connector; 

    public ChannelWatchDog(IConnector<IConnectionMaintainable> connector /*various other parameters*/) 
    { 
     this.connector = connector; 
    } 

    // ...methods that use connector's Connect, Disconnect, and GetServerTime methods 
} 

public class ConnectorBuilder<T> : IConnectorBuilder<T> where T:IConnectionMaintainable 
{ 
    private string endpoint; 

    public ConnectorBuilder(string endpoint) 
    { 
     this.endpoint = endpoint; 
    } 
    public IConnectorBuilder<T> MakeReliable() 
    { 
     // save various parameters required to build the ChannelWatchDog 
     return this; 
    } 
    public IConnector<T> GetConnector() 
    { 
     Connector<T> connector = new Connector<T>(endpoint); 

     // If reliability was requested, build the ChannelWatchDog: Following line does not compile: 
     ChannelWatchDog watchDog = new ChannelWatchDog((IConnector<IConnectionMaintainable>)connector); 

     return connector; 
    } 

    public IConnectorBuilder<TReliable> MakeReliable<TReliable>() where TReliable : T, IConnectionMaintainable 
    { 
     // save various parameters required to build the ChannelWatchDog 
     return (IConnectorBuilder<TReliable>)this; 
    } 
} 

public class Connector<T> : IConnector<T> where T:IConnectionMaintainable 
{ 
    private T channel; 

    public Connector(string endpoint) 
    { 
     // ...build channel 
    } 

    public T Channel 
    { 
     get { return channel; } 
    } 

    public void Connect() 
    { 
     // ...connect to server 
    } 

    public void Disconnect() 
    { 
     // ...disconnect from server 
    } 
} 
0

你應該在類ConnectorBuilder泛型參數T實現接口IConnectionMaintainable。原因是通用類型的ChannelWatchDog需要它。通用T對於ChannelWatchDog的泛型類型參數不夠嚴格。

1

有可能使用反射來允許將Connector<T>轉換爲Connector<IConnectionMaintainable>,但我不確定這將是多麼強大的性能,它需要能夠克隆Connector<T>類。

public class Connector<T> : IConnector<T> 
{ 
    // ... 
    private Connector() 
    { 
    } 
    // ... 
    public static explicit operator Connector<IConnectionMaintainable>(Connector<T> other) 
    { 
     Connector<IConnectionMaintainable> connector = null; 

     Type p = typeof(T); 
     if (p.GetInterfaces().Contains(typeof(IConnectionMaintainable))) 
     { 
      connector = new Connector<IConnectionMaintainable>(); 
      connector.channel = other.channel as IConnectionMaintainable; 
     } 
     else 
     { 
      throw new InvalidCastException(); 
     } 

     return connector; 
    } 
} 

public class ConnectorBuilder<T> : IConnectorBuilder<T> 
{ 
    // ... 
    public IConnector<T> GetConnector() 
    { 
     Connector<T> connector = new Connector<T>(endpoint); 

     // If reliability was requested, build the ChannelWatchDog: 
     try 
     { 
      Connector<IConnectionMaintainable> temp = (Connector<IConnectionMaintainable>)connector; 
      ChannelWatchDog<IConnectionMaintainable> watchDog = new ChannelWatchDog<IConnectionMaintainable>(temp); 
     } 
     catch (InvalidCastException) 
     { 
      throw new ArgumentException("Trying to make reliable when not possible"); 
     } 

     return connector; 
    } 
    // ... 
}