C# upcast generic type to interface - c#

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).

Related

Cannot implement interface member using derived type

Lets assume I've got a class Called StreetModel that implements an interface called IStreetModel:
public class StreetModel: IStreetModel {
// Properties go here
}
Said IStreetModel looks like so:
public interface IStreetModel {
// Properties go here
}
Now, say I have another interface called ILocationModel that contains a property of type IStreetModel:
public interface ILocationModel {
IStreetModel Street { get; }
}
And this ILocationModel interface is implemented by a class called LocationModel:
public class LocationModel: ILocationModel {
public StreetModel Street { get; } // This is where my query is
}
My Question: Within LocationModel, why can't I implement StreetModel even though it implements IStreetModel. Why does the compiler want IStreetModel specifically? It's confusing for me because in almost every other scenario within my program (except for collections) I can interchange usage of the two. Why not here?
Error Message:
'LocationModel' does not implement interface member
'ILocationModel.Street'. 'LocationModel.Street' cannot implement
'ILocationModel.Street' because it does not have the matching return
type of 'IStreetModel'.
The answer to your actual question ("why doesn't it compile?") is: The c# language requires that a return type for an implemented interface member matches exactly. That's the way the language was designed.
This was fixed by c# 9 in some cases, but not for interface implementations. See the section following The remainder of the draft specification below proposes a further extension to covariant returns of interface methods to be considered later in this documentation.
In the meantime, a possible workaround is to make your ILocationModel generic like so:
public interface IStreetModel
{
// Properties go here
}
public interface ILocationModel<out T> where T: IStreetModel
{
T Street { get; }
}
public class StreetModel : IStreetModel
{
// Properties go here
}
public class LocationModel : ILocationModel<StreetModel>
{
public StreetModel Street { get; } // This is where my query is
}
Of course the most obvious solution is simply to declare the return type of LocationModel.Street as IStreetModel.
Another alternative is to use explicit interface implementation, like so:
public class LocationModel : ILocationModel
{
public StreetModel Street { get; } // This is where my query is
IStreetModel ILocationModel.Street => Street;
}

How to implement a generic interface with a child generic interface

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.

How do I pass two similar concrete objects to a method with interface parameters that implement generics in C#?

I have the following interface declarations:
interface IOrder<T> where T: IOrderItem
{
IList<T> Items { get; set; }
}
interface IDistro<T> : IOrder<T> where T: IOrderItem
{
}
I have two concrete classes, like so:
// DistroItem implements IOrderItem
public class Distro : IDistro<DistroItem>
{
public IList<DistroItem> Items { get; set; }
}
// PerishableOrderItem implements IOrderItem
public class PerishableOrder : IDistro<PerishableOrderItem>
{
public IList<PerishableOrderItem> Items { get; set; }
}
Lastly, I have a static service method for saving to the database:
public static void UpdateDistro(IDistro<IOrderItem> distro)
{
}
My problem is, how do I pass a distro of either concrete type to my static method? The following doesn't compile:
Distro d = new Distro();
UpdateDistro(d);
The error is:
The best overloaded method match for UpdateDistro(IDistro<IOrderItem>)' has some invalid arguments
Is contravariance the answer? I tried adding <in T> to the original interface declaration, but that added more errors that I was unable to resolve. This is my first in depth foray into interfaces and I'm sure generics is adding complexity, so there might be a fundamental lack of understanding here.
Have you tried this:
public static void UpdateDistro<T>(IDistro<T> distro)
where T : IOrderItem
{
}
EDIT:
With empty implementations for DistroItem and PerishableItem classes (both implementing IOrderItem), I've got the following compiling without an error:
Distro d = new Distro();
PerishableOrder p = new PerishableOrder();
UpdateDistro(d);
UpdateDistro(p);
You can define a covariant generic parameter in your interface, you need to change the interface a little bit though to ensure that T is not uses contravariantly:
public interface IOrder<out T> where T : IOrderItem
{
IEnumerator<T> Items { get; }
}
public interface IDistro<out T> : IOrder<T> where T : IOrderItem
{
}
To define T as coverient parameter (out), allows for implicit conversion of classes that implement your variant interfaces.

How to cast a generic class to a generic interface in C#

according to below definitions
interface myin
{
int id { get; set; }
}
class myclass:myin
{
public int id { get; set; }
}
[Database]
public sealed class SqlDataContext : DataContext, IDataContext
{
public SqlDataContext(string connectionString) : base(connectionString){}
public ITable<IUrl> Urls
{
get { return base.GetTable<Url>(); } //how to cast Table<Url> to ITable<IUrl>?
}
...
}
Update:
public IEnumerable<IUrl> Urls
{
get { return base.GetTable<Url>(); }
}
so by use above approach, i haven't Table class associated methods and abilities. this is good solution or not? and why?
In C# 3.0 and odler this is not easily possible - see also covariance and contravariance in C# 4.0.
The problem is that Table<Url> implements the ITable<Url> interface - this part of the casting is easy. The tricky bit is casting ITable<Url> to ITable<IUrl>, because these two types aren't actually related in any way...
In C# before 4.0, there is no easy way to do this - you'll explicitly need to create a new implementation of ITable<..> for the right generic type (e.g. by delegation). In C# 4.0, this conversion can be done as long as ITable is a covariant interface.

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