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.
Related
It's already been covered in a few topics that empty interfaces that are used as markers where identification occurs at run time are code smell, but what about when you have something like this:
public interface IPropertyRetriever<out T>
{
public string Name { get; }
public T Retrieve();
}
public interface IComparablePropertyRetriever<out T> : IPropertyRetriever<T> where T : IComparable
{
}
In this example, although IComparablePropertyRetriever<T> is an empty interface, it does add a constraint to what T can be. Would something like this also be considered code smell?
EDIT
Ok so here is both how I'm using it and the use case.
I'm building an expression parser that consists of an expression, an operator and a constant, so I ended up with this:
public interface IOperator<out T>
{
public IPropertyRetriever<T> LeftObjectRetriever { get; }
public T RightObject { get; }
public bool Compare();
}
T can only ever be one of 4 types: string, bool?, long? and Version
My operators are things like StartsWith, GreaterThan, LowerEquals, Contains...
Clearly some operators such as StartsWith and Contains only work on strings and that's pretty simple to do, I can just do this:
public class BeginsWith : IOperator<string>
{
public BeginsWith(IPropertyRetriever<string> leftObjectRetriever, string rightObject)
{
LeftObjectRetriever = leftObjectRetriever;
RightObject = rightObject;
}
public IPropertyRetriever<string> LeftObjectRetriever { get; }
public string RightObject { get; }
public bool Compare()
{
throw new System.NotImplementedException();
}
}
But others, such as LowerThan work on all other types, except string. And although I guess I could do something like this (which is ultimately what the IComparablePropertyRetriever<T> interface is doing)
public class LowerThan : IOperator<IComparable>
{
}
It still allows me to pass a string to LowerThan since string implements IComparable
By shifting the responsability to the classes implementing IPropertyRetriever (which I can control far more easily) I can instead do something like this:
public class FileVersionRetriever : IComparablePropertyRetriever<Version>
{
}
And implement LowerThan as follows:
public interface IComparableOperator : IOperator<IComparable>
{
public new IComparablePropertyRetriever<IComparable> LeftObjectRetriever { get; }
}
public class LowerThan : IComparableOperator
{
}
And I guess now that I've explained that the whole question turned into something completely different which is how to implement this better xD
I have some base classes like this:
public class AbstractData
{
public int ID { get; set; }
}
public class Person: AbstractData
{
public string Name { get; set; }
}
public class AbstractManager<T> where T: AbstractData
{
public virtual List<T> GetAll()
{
}
public virtual T GetOne(int id)
{
}
}
public class PersonManager: AbstractManager<Person>
{
public override List<Person> GetAll()
{
//...
}
public override Person GetOne(int id)
{
//...
}
}
Now, I have a Windows Forms base class, like this:
public class BaseForm: Form
{
public virtual AbstractManager<T> GetManager<T>() where T: AbstractData
{
return null;
}
}
and a derived form:
public class PersonForm: BaseForm
{
public override AbstractManager<T> GetManager<T>()
{
return new PersonManager();
}
}
The problem is, I keep getting compile errors on the PersonForm class:
Cannot implicitly convert type 'PersonManager' to 'AbstractManager<T>'
Is there a way in which I can create this virtual method and have every class derived from BaseForm return the concrete representation of the AbstractManager?
If I get rid of the generic on the AbstractManager class then I compile OK (with a few code changes), but then the GetAll method can't return a List<T>. It would have to return a List<AbstractData> instead, which causes issues in converting from List<Person> to List<AbstractData>.
Any help would be appreciated.
First off all, please never do this:
class C<T>
{
void M<T>(T t) { }
}
Now we have two things named T both in scope and they are different. This is legal but extremely confusing. Choose better names for your type parameters.
Let's simplify your example:
class FruitBasket<T> where T : Fruit { }
class AppleBasket : FruitBasket<Apple> { }
class C
{
public static FruitBasket<T> GetBasket<T>() where T: Fruit
{
return new AppleBasket();
}
}
Now do you see why this is wrong? What if someone calls C.GetBasket<Orange>() and you hand them a basket of apples?
Any help would be appreciated.
What's step one of getting out of a hole? STOP DIGGING.
You have Genericity Happiness Disease, which is common to C# programmers who are discovering the power of the generic type system and then want to use it for everything whether that makes sense or not. Stop trying to capture all the relationships in your business process in the generic type system; that's not what it was designed for.
The test is: can you say "an apple basket is a basket of apples, where apples are a kind of fruit" and have someone who is not a programmer agree with you? Yes. Can you say "a person manager is an abstract manager of persons where person is a kind of abstract data" and have someone who is not a programmer agree with you? No. Then you are not successfully modeling the business domain in the type system. Start over, avoid generics, and try to come up with relationships between types that make sense.
By declaring
public virtual AbstractManager<T> GetManager<T>() where T: AbstractData
in BaseForm, you're promising that every class derived from BaseForm supports GetManager for any type T. For example, if you had another AbstractData subclass named Invoice, then you could write
personForm.GetManager<Invoice>()
and PersonForm would be expected to return an InvoiceManager.
If you want every class derived from BaseForm to support GetManager for only one type T, then move the T type parameter from GetManager to BaseForm:
public class BaseForm<T>: Form where T: AbstractData
{
public virtual AbstractManager<T> GetManager()
{
return null;
}
}
public class PersonForm: BaseForm<Person>
{
public override AbstractManager<Person> GetManager()
{
return new PersonManager();
}
}
UPDATE: Chad Henderson points out that the Windows Forms designer can't handle generic base classes. If that's a problem for you, then you could try an alternate approach:
public interface IForm<T> where T: AbstractData
{
AbstractManager<T> GetManager();
}
public class BaseForm: Form
{
// ... base functionality that doesn't depend on T ...
}
public class PersonForm: BaseForm, IForm<Person>
{
public AbstractManager<Person> GetManager()
{
return new PersonManager();
}
}
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.
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).
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.