2017-04-21 46 views
1

我一直在使用接口,繼承和泛型的c#寫下面的代碼:C#泛型協方差和逆變衝突

public interface IBasic 
{ 

} 

public class Basic : IBasic 
{ 

} 

public class AnotherBasic : Basic 
{ 

} 

public interface IWorker<in TBasic> 
{ 
    void Run(TBasic basic); 
} 

public class Worker : IWorker<Basic> 
{ 
    public void Run(Basic basic) 
    { 
     throw new System.NotImplementedException(); 
    } 
} 

public class AnotherWorker : IWorker<AnotherBasic> 
{ 
    public void Run(AnotherBasic basic) 
    { 
     throw new System.NotImplementedException(); 
    } 
} 

public void Test() 
{ 
    List<IWorker<IBasic>> workers = new List<IWorker<IBasic>> 
    { 
     new Worker(), 
     new AnotherWorker() 
    }; 
} 

這段代碼的問題是,工人和anotherworker類不適合於仿製藥名單IWorker<IBasic>誰是工人和基本班的工人的父母。事情是IWorker<in TBasic>因爲運行方法簽名是逆變的,但是我需要它協變,以便填寫List<IWorker<IBasic>>。運行方法必須具有TBasic參數,並且我需要這個職責列表來實現責任鏈設計模式。我是否錯過了某些東西,或者是否找到了使協變和逆變不互相排斥的理由?

+5

你不能這樣做,因爲它不安全,例如'新的列表> {新的AnotherWorker()} [0] .Run(新的基本())'將編譯,但在運行時失敗。 – Lee

+3

這看起來像[XYProblem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)。無論您嘗試使用此設計解決什麼問題,如果您描述您嘗試解決的實際問題,都可能用不同的設計解決。 – juharr

+0

我明顯可以通過多種方法解決這個問題,但是我希望這能夠以我顯示的方式工作 –

回答

0

所以經過學習和調查2天我回答我自己的問題。下面是代碼:

公共接口IBASIC {

} 

public class Basic : IBasic 
{ 

} 

public class AnotherBasic : Basic 
{ 

} 

public interface IWorker<in TBasic> 
{ 
    void Run(TBasic basic); 
} 

public class SimpleWorker : IWorker<IBasic> 
{ 
    public void Run(IBasic basic) 
    { 
     throw new System.NotImplementedException(); 
    } 
} 

public class Worker : IWorker<Basic> 
{ 
    public void Run(Basic basic) 
    { 
     throw new System.NotImplementedException(); 
    } 
} 

public class AnotherWorker : IWorker<AnotherBasic> 
{ 
    public void Run(AnotherBasic basic) 
    { 
     throw new System.NotImplementedException(); 
    } 
} 

public class Final 
{ 
    public void Test() 
    { 
     List<IWorker<AnotherBasic>> workers = new List<IWorker<AnotherBasic>> 
     { 
      new SimpleWorker(), 
      new Worker(), 
      new AnotherWorker() 
     }; 
    } 
} 

TBasic在逆變,這意味着聲明應該是最具體地,如圖中的代碼:AnotherBasic 然後是類型少派生,是父母,被接受,代碼編譯。

0

您可以像這樣初始化:

public void Test() 
{ 
    List<IWorker<IBasic>> workers = new List<IWorker<IBasic>> 
    { 
     new Worker<IBasic>(), 
     new AnotherWorker<IBasic>() 
    }; 
    workers[0].Run(new Basic()); 
} 
+0

不,它不工作,工作人員和其他工作人員都不是通用的 –

0

你的工人的聲明是說:「這是工作人員的名單,而且每一個可以運行IBASIC的任何實現」,這是不正確的。

你可以嘗試的是將工人可以處理的這種命令的責任轉移給工人本身(實際上,這就是chain of responsibility pattern所暗示的)。

public interface IWorker 
{ 
    bool DidRun<TBasic>(TBasic basic); 
} 

public class WorkerChain 
{ 
    private readonly List<IWorker> workers = new List<IWorker> 
    { 
     new Worker(), 
     new AnotherWorker() 
    }; 

    public bool DidRun<T>(T basic) 
    { 
     return workers.Any(worker => worker.DidRun(basic)); 
    } 
} 

public class Worker : IWorker 
{ 
    public bool DidRun<T>(T basic) 
    { 
     if (!(basic is Basic)) 
     { 
      return false; 
     } 

     Console.WriteLine($"running {basic}"); 
     return true; 
    } 
} 

public class Test 
{ 
    public void CanRunWorkBasic() 
    { 
     var didRun = new WorkerChain().DidRun(new Basic()); 
     Debug.Assert(didRun); 
    } 
} 
0

如果要插入你的工人到列表中,您將需要一個非通用接口IWorkerIWorker<TBasic>必須實現該接口。然後在List中使用IWorker而不是IWorker<TBasic>。現在,將您的工作人員添加到List不存在更多問題。

這樣我們解決了一個問題,但不幸的是我們也創建了另一個問題,因爲我們必須實施兩次Run方法。一次爲非通用接口,第二次爲通用接口。

您可以使用抽象Worker類,默認情況下,當非通用Run被調用,使得neccessary檢查攻克的難題,注塑參數,並將其傳遞給通用Run方法。然後你的工作人員可以從Worker進行驅動,每個人都可以有自己的角色。

在下面的例子中,我試圖展示,在我看來,這個代碼應該如何。非通用Run方法實施explicitly爲了安全起見,我還使用了generic type constraintsRun方法只是檢查類型並進一步傳遞它。

public interface IBasic 
{ 

} 

public class Basic : IBasic 
{ 

} 

public class AnotherBasic : Basic 
{ 

} 

public interface IWorker 
{ 
    void Run(IBasic basic); 
} 

public interface IWorker<in TBasic> : IWorker where TBasic : IBasic 
{ 
    void Run(TBasic basic); 
} 

public abstract class Worker<TBasic> : IWorker<TBasic> where TBasic : IBasic 
{ 
    void IWorker.Run(IBasic basic) 
    { 
     if (basic is TBasic) 
      Run((TBasic)basic); 
    } 

    public abstract void Run(TBasic basic); 
} 

public class FirstWorker : Worker<Basic> 
{ 
    public override void Run(Basic basic) 
    { 
     // ... 
    } 
} 

public class SecondWorker : Worker<AnotherBasic> 
{ 
    public override void Run(AnotherBasic basic) 
    { 
     // ... 
    } 
} 


public void Test() 
{ 
    List<IWorker> workers = new List<IWorker> 
    { 
     new FirstWorker(), 
     new SecondWorker() 
    }; 
}