Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>So this is a fairly common problem with generics. </p> <p>imagine 2 classes <code>class Base</code> and <code>class A</code>. <code>class A</code> descends from <code>Base</code>.</p> <pre><code> public class Base { } public class A : Base { } </code></pre> <p>Now consider <code>List&lt;T&gt;</code>. You can make classes from <code>List&lt;T&gt;</code> to hold Base or A:</p> <pre><code>List&lt;Base&gt; x = new List&lt;Base&gt;(); </code></pre> <p>and </p> <pre><code>List&lt;A&gt; y = new List&lt;A&gt;(); </code></pre> <p>It is a common misconception that the class of y must be a descendant of the class of x, but this cannot be true because x has a methods like <code>Add(Base item)</code> and y has a methods like <code>Add(A item)</code> and it is not possible for the compiler to guarantee that on y the interface will be compatible with the interface of x. this is because if you treat an instance of <code>List&lt;A&gt;</code> as an instance of <code>List&lt;Base&gt;</code> there is nothing to stop Add being called with an instance of Base or another subclass of Base. </p> <p>Now there are some parts of the interface that can be guarantee as compatible. They are any parts that return an instance of class A, since A can always take the place of Base. </p> <p>If your interface only output the generic and you are using .net 4 there is an easy solution. The out generic modifier:</p> <pre><code> public class Example { private readonly Dictionary&lt;Type, IRepository&lt;Base&gt;&gt; _repositoriesCollection = new Dictionary&lt;Type, IRepository&lt;Base&gt;&gt;(); public void DoSomething() { _repositoriesCollection.Add(typeof(A), new Repository&lt;A&gt;()); } } interface IRepository&lt;out T&gt; where T : Base { T MakeSomeItem(string info); //void AddSomeItem(string info, T itemToAdd); &lt;- this will not // work because T // is out - so can't // go in... IEnumerable&lt;T&gt; MakeSomeListOfItems(); // This is OK because // IEnumerable&lt;T&gt; is declared as IEnumerable&lt;out T&gt; in the fx //List&lt;T&gt; Something(); &lt;- not ok because List&lt;T&gt; is not List&lt;out T&gt; } public class Repository&lt;T&gt; : IRepository&lt;T&gt; where T : Base { public T MakeSomeItem(string info) { throw new NotImplementedException(); } public IEnumerable&lt;T&gt; MakeSomeListOfItems() { throw new NotImplementedException(); } } public class Base { } public class A : Base { } </code></pre> <p>This solution will not work for 2 cases; when you need to pass an item into you interface and when you are not using .net 4. </p> <p>There are numerous different solutions for both of those case also.</p> <p>1) I need to pass an item into the interface too and I am using .net 4 - just pass it as Base, if you need to maintain type safety wrap it with a generic method somewhere else. </p> <pre><code> interface IRepository&lt;out T&gt; where T : Base { T MakeSomeItem(string info); void AddSomeItem(string info, Base itemToAdd); } public class Repository&lt;T&gt; : IRepository&lt;T&gt; where T : Base { public T MakeSomeItem(string info){ throw new NotImplementedException(); } public void AddSomeItem(string info, Base itemToAdd) { T castedItem = (T) itemToAdd; //fails here at //run time if not // correct type AddSomeItem(info, itemToAdd); } public void AddSomeItem(string info, T itemToAdd) { /// do it for real... } } </code></pre> <p>2) If you are not working with .net 4 then there are other things that you can do too, force the repository to implement the Base version of your interface: </p> <pre><code>interface IRepository&lt;T&gt; where T : Base { T MakeSomeItem(string info); void AddSomeItem(string info, T itemToAdd) } public class Repository&lt;T&gt; : IRepository&lt;Base&gt;, IRepository&lt;T&gt; where T : Base { public T MakeSomeItem(string info) { throw new NotImplementedException(); } public void AddSomeItem(string info, Base itemToAdd) { T castedItem = (T) itemToAdd; //fails here at //run time if not // correct type AddSomeItem(info, itemToAdd); } public void AddSomeItem(string info, T itemToAdd) { /// do it for real... } Base IRepository&lt;Base&gt;.MakeSomeItem(string info) { return MakeSomeItem(info); } } </code></pre> <p>There is still yet more that you can do if you want to keep your input strongly typed - but I think my answer is long enough for now.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload