2017-04-21 45 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() 
    }; 
}