How to implement a generic interface with a child generic interface - c#

I'm having an issue with implementing parent/child interfaces when both of them are generic. The best answer I've been able to find is that it isn't possible, but I also haven't been able to find anyone else asking the exact same question. I'm hoping that I just don't know the right syntax to make the compiler understand what I'm trying to do. Here is a stripped down example of the code I'm trying to implement.
public interface I_Group<T>
where T : I_Segment<I_Complex>
{
T Segment { get; set; }
}
public interface I_Segment<T>
where T : I_Complex
{
T Complex { get; set; }
}
public interface I_Complex
{
string SomeString { get; set; }
}
public partial class Group : I_Group<Segment>
{
private Segment segmentField;
public Group() {
this.segmentField = new Segment();
}
public Segment Segment {
get {
return this.segmentField;
}
set {
this.segmentField = value;
}
}
}
public partial class Segment : I_Segment<Complex> {
private Complex complexField;
public Segment() {
this.complexField = new Complex();
}
public Complex Complex {
get {
return this.c_C001Field;
}
set {
this.c_C001Field = value;
}
}
}
public partial class Complex : I_Complex {
private string someStringField;
public string SomeString {
get {
return this.someStringField;
}
set {
this.someStringField = value;
}
}
}
So here, Complex is the grandchild, which implements I_Complex without error. Segment is its parent, which implements I_Segment without error. The issue is with the grandparent, Group, trying to implement I_Group. I get the error
The type 'Segment' cannot be used as type parameter 'T' in the generic type or method 'I_Group<T>'. There is no implicit reference conversion from 'Segment' to 'I_Segment<I_Complex>'.
I am led to believe this is an issue with covariance, but I was also led to believe this was something that was supposed to work in C# 4.0. This works when the child isn't generic, which leads me to think that there must exist some syntax to get this to compile properly. Am I doing something wrong? Is this even possible? And if not, could someone help me understand why not?

You can add second generic type parameter into I_Group interface declaration:
public interface I_Group<T, S>
where T : I_Segment<S>
where S : I_Complex
{
T Segment { get; set; }
}
And specify explicitly both types in Group class declaration:
public partial class Group : I_Group<Segment, Complex>
It will make your code compile.

Well, to get covariance or contravariance to work with an interface, you use the "in" and "out" keywords. Covariance uses the out keyword, for example:
public interface A<out T>
{
T Foo();
}
While contravariance uses the in keyword:
public interface B<in T>
{
Bar( T t );
}
The problem in your case is that your I_Segment interface is not covariant or contravariant, so I_Segment is not compatible with I_Segment, which is why you get a compile error.

Related

Generic Interface type conversion issues

I have been battling with this bit of code for a while now and I am trying to get a solution as it is literally the last part before it goes to testing.
I have the following interfaces and classes (simplified to the relevant parts):
public interface ITagParent<T> where T : ITag
{
List<TagAddOn<T>> TagCollection { get; set; }
}
public interface ITag
{
int Id { get; set; }
string Description { get; set; }
TagGroup TagGroup { get; set; }
}
public class TagAddOn<T> : ViewModelBase where T : ITag
{
private T _currentTag;
public T CurrentTag
{
get { return _currentTag; }
set { _currentTag = value; }
}
}
public partial class Customer : ITagParent<CustomerTag>
{
List<TagAddOn<CustomerTag>> _tagCollection;
public List<TagAddOn<CustomerTag>> TagCollection
{
get { return _tagCollection; }
set { _tagCollection = value; }
}
}
public partial class CustomerTag : ITag
{
public int Id { get; set; }
}
public class TagAddOnManager
{
public static string GetTagCurrentValue(List<TagAddOn<ITag>> dataObjectAddOns)
{
// LOTS OF SNIPPING!
return string.Empty;
}
}
I am trying to use the GetTagCurrentValue method in the TagAddOnManager class like this:
string value = TagAddOnManager.GetTagCurrentValue(
((ITagParent<ITag>)gridCell.Row.Data).TagCollection));
Everything compiles fine, but errors when trying to cast gridCell.Row.Data to ITagParent<ITag>. I understand this is due to covarience and a workaround (if not a terribly safe one) is to mark T in the ITagParent interface with the out keyword, but that won't work as you can see it is used in the TagCollection property, which can't be read only.
I tried casting the above to ITagParent<CustomerTag>, but this fails at compile time with a 'cannot convert' error when trying to feed it into my GetTagCurrentValue method.
Another option I considered is using some base classes instead of the ITagParent interface, but that won't work as the Customer object already inherits from another base class, which can't be modified for this implementation.
I know I could just overload the GetTagCurrentValue method with List<TagAddOn<CustomerTag>> as the parameter type and all other variations, but that really seems like a 'I give up' solution. I could probably use reflection to get the desired results, but that would be unwieldy and not very efficient, especially considering this method could be called a lot in a particular process.
So does anyone have any suggestions?
Could you use something like that
public class TagAddOnManager
{
public static string GetTagCurrentValue<TTag>(ITagParent<TTag> tagParent)
where TTag : ITag
{
// Just an example.
return tagParent.TagCollection.First().CurrentTag.Description;
}
}
and use it like that?`
var value = TagAddOnManager.GetTagCurrentValue((Customer)CustomergridCell.Row.Data);

interface as return type

Can interface be a return type of a function. If yes then whats the advantage. e.g. is the following code correct where array of interface is being returned.
public interface Interface
{
int Type { get; }
string Name { get; }
}
public override Interface[] ShowValue(int a)
{
.
.
}
Yes, you can return an interface.
Let's say classes A and B each implement interface Ic:
public interface Ic
{
int Type { get; }
string Name { get; }
}
public class A : Ic
{
.
.
.
}
public class B : Ic
.
.
.
}
public Ic func(bool flag)
{
if (flag)
return new A();
return new B();
}
In this example func is like factory method — it can return different objects!
Yes an interface is a very valid return value. Remember, you're not returning the definition of the interface, but rather an instance of an implementation of that interface.
The benefit is very clear, consider the following code:
public interface ICar
{
string Make { get; }
}
public class Malibu : ICar
{
public string Make { get { return "Chevrolet"; } }
}
public class Mustang : ICar
{
public string Make { get { return "Ford"; } }
}
now you could return a number of different ICar instances that have their own respective values.
But, the primary reason for using interfaces is so that they can be shared amongst assemblies in a well-known contract so that when you pass somebody an ICar, they don't know anything about it, but they know it has a Make. Further, they can't execute anything against it except the public interface. So if Mustang had a public member named Model they couldn't get to that unless it was in the interface.
Yes it can.
The benefit is that you can abstract the return (and input) types.
public interface IFruit{ }
public class Apple: IFruit{}
public class Pear: IFruit{}
...
public function IFruit SelectRandomFromBasket(Basket<IFruit> basket){
// this function can return Apple, Pear
}
Yes it's possible, and it's a good thing for public functions.
For the why part of your question, I won't copy paste other people answers, so please refer to existing answers, for example:
List<T> or IList<T>
Method return an interface
https://stackoverflow.com/a/3564291/870604
Yes, I will answer your question in golang perspective where we can use interface as a return type that means you can return any datatype e.g int, float, string etc.
interface = any datatype
package main
import (
"fmt"
)
func any (u interface{}) interface{} {
fmt.Printf("Type: %T\n",u)
return u
}
func main(){
fmt.Println("value: ",any(5))
fmt.Println(any("testing"))
}
Output

C# upcast generic type to interface

This is similar to another topic I recently posted, but perhaps this might be simpler and clearer:
I want to accomplish the following (or something very similar)...
IManageableEntryDao<IManageableEntry> dao = new CompanyNameDao();
... with the following classes:
public interface IManageableEntry {
string Name { get; set; }
}
public class CompanyName : IManageableEntry {
public string Name { get; set; }
}
public interface IManageableEntryDao<T> where T : IManageableEntry {
}
public class CompanyNameDao : IManageableEntryDao<CompanyName> {
}
If I try to do a cast as IManageableEntryDao<IManageableEntry>, I get a null.
I believe you need covariance for this to work. This feature is only available in C# 4.0. What you need to do:
public interface IManageableEntryDao<out T> where T : IManageableEntry { }
See Variance in Generic Interfaces. Change the interface to IManageableEntryDao<out T> and it should work (unless the interface uses it in a way which makes this invalid).

Implementing IEnumerable<T> in List<T> derived Collection

I'm getting an error. Here's the code copied across to a Console project and stripped down:
namespace ConsoleApplication1
{
public interface IHexGrid
{
IEnumerable<Hex> hexs { get; } //error related location
}
public class HexC : Hex
{ public int var1;}
public abstract class Hex
{ public int var2; }
public class HexGridC : IHexGrid //error CS0738
{
public List<HexC> hexs { get; set; } // error related location
}
class Program
{
static void Main(string[] args)
{
}
}
}
I'm getting the following: error CS0738:
'ConsoleApplication1.HexGridC' does not implement interface
member 'ConsoleApplication1.IHexGrid.hexs'. 'ConsoleApplication1.HexGridC.hexs' cannot
implement 'ConsoleApplication1.IHexGrid.hexs' because it does not have the matching
return type of '`System.Collections.Generic.IEnumerable<ConsoleApplication1.Hex>`'.
Not sure why as IENumerable is Covariant. Any help much appreciated.
Edit: the code has been simplified
The problem is that your property is of the wrong type. C# doesn't support covariant return types for properties or methods specified in interfaces or for virtual method overriding. You can use explicit interface implementation though:
public class HexGridC : IHexGrid //error CS0738: etc
{
public GridElList<HexC> hexs { get; set; } // error related location
IEnumerable<Hex> IHexGrid.hexs { get { return hexs; } }
}
As an aside, this all seems terribly complicated code - and it's generally not a good idea to derive from List<T> in the first place. (Favour composition, or derive from Collection<T> which is designed for inheritance.) Does it really need to be so complicated? If it does, it would still have been worth cutting down the complexity of the example for the sake of the question.

c# - cast generic class to its base non-generic class

I have following classes:
public abstract class CustomerBase
{
public long CustomerNumber { get; set; }
public string Name { get; set; }
}
public abstract class CustomerWithChildern<T> : CustomerBase
where T: CustomerBase
{
public IList<T> Childern { get; private set; }
public CustomerWithChildern()
{
Childern = new List<T>();
}
}
public class SalesOffice : CustomerWithChildern<NationalNegotiation>
{
}
The SalesOffice is just one of few classes which represent different levels of customer hierarchy. Now I need to walk through this hierarchy from some point (CustomerBase). I can't figure out how to implement without using reflection. I'd like to implement something like:
public void WalkHierarchy(CustomerBase start)
{
Print(start.CustomerNumber);
if (start is CustomerWithChildern<>)
{
foreach(ch in start.Childern)
{
WalkHierarchy(ch);
}
}
}
Is there any chance I could get something like this working?
The solution based on suggested has-childern interface I implemented:
public interface ICustomerWithChildern
{
IEnumerable ChildernEnum { get; }
}
public abstract class CustomerWithChildern<T> : CustomerBase, ICustomerWithChildern
where T: CustomerBase
{
public IEnumerable ChildernEnum { get { return Childern; } }
public IList<T> Childern { get; private set; }
public CustomerWithChildern()
{
Childern = new List<T>();
}
}
public void WalkHierarchy(CustomerBase start)
{
var x = start.CustomerNumber;
var c = start as ICustomerWithChildern;
if (c != null)
{
foreach(var ch in c.ChildernEnum)
{
WalkHierarchy((CustomerBase)ch);
}
}
}
You could move the WalkHierarchy method to the base class and make it virtual. The base class implementation would only process the current node. For the CustomerWithChildern<T> class, the override would do an actual walk.
Try this:
if(start.GetType().GetGenericTypeDefinition() == typeof(CustomerWithChildern<>))
I believe that you want to make the lookup for the determination of doing to the walk an interface.
So maybe add an "IWalkable" interface that exposes the information needed to do the walk, then you can create your method checking to see if the passed object implements the interface.
"Is" and "As" only work on fully qualified generic types.
See this MSDN discussion for details including workarounds.
The most common workaround I've seen is to add an interface to the mix that your CustomerWithChildren could implement, and check for that interface.
I think everyone hits this "issue" when first working with generic classes.
Your first problem is hinted at in your question phrasing: an open generic type is NOT the base class to a closed one. There is no OO relationship here, at all. The real base class is CustomerBase. An "open" generic type is like a half-completed class; specifying type arguments "closes" it, making it complete.
While you can do:
Type t = typeof(CustomerWithChildern<>)
the condition
typeof(CustomerWithChildern<>).IsAssignableFrom(CustomerWithChildern<Foo>)
will always be False.
-Oisin
Explicitly with that method, no. However you can achieve the same functionality with an interface. In fact, you could just have your generic class implement IEnumerable. It's also worth noting that your class should also have "where T : CustomerBase" in order to ensure type safety.

Categories

Resources