List with Multiple Generic Objects (Covariance Issue) - c#

I've been trying to get this solved, and have looked at the similar issues, but am not getting the proper solution here.
I am using a Generic class to hold configuration for an export
public class SpreadsheetConfiguration<T> where T : class
{
public IEnumerable<T> ExportData {get; set;}
// More stuff here
}
I have a situation where I need a list of these, which might not be of the same type, for example like this
public byte[] ExportMultipleSheets(IEnumerable<SpreadsheetConfiguration<object>> toExport)
But I cannot for the life of me, figure out how to make this work and I've looked at the other routes above with regards to making an ISpreadsehetConfiguration or otherwise.
This is for an OpenSource project here: https://github.com/IowaComputerGurus/netcore.utilities.spreadsheet
I know I'm missing something, but I've tried all of the above, and still not get to a final usage where I can still do
var toExport = new SpreadsheetConfiguration<MyOtherClass>();
As it fails to convert

If your class is supposed to have a setter on that IEnumerable<T> then it cannot be covariant. Covariance is read-only, contravariance is write-only. If you need both and also need a collection of such configurations, then your design is flawed.
If you are willing to have only get access on that property, then you first need to make an interface for your class, since variance works on generic interfaces only:
public interface ISpreadsheetConfiguration<out T> where T : class
{
IEnumerable<T> ExportData { get; }
}
public class SpreadsheetConfiguration<T> : ISpreadsheetConfiguration<T> where T : class
{
public IEnumerable<T> ExportData {get; set;}
}
Notice the out keyword in the interface's type parameter declaration - it means that ISpreadsheetConfiguration<T> is covariant in T.
Now you can do this:
public byte[] ExportMultipleSheets(IEnumerable<ISpreadsheetConfiguration<object>> toExport);
var toExport = new ISpreadsheetConfiguration<object>[]
{
new SpreadsheetConfiguration<MyOtherClass>(),
new SpreadsheetConfiguration<CompletelyDifferentClass>()
};
ExportMultipleSheets(toExport);
More on variance and why covariance cannot work with a type that allows both reads and writes with type T here.

Related

How to cast A<T> to A<object>? [duplicate]

I know this is old, yet I am still not very good with understanding those problems. Can anyone tell me why the following does not work (throws a runtime exception about casting)?
public abstract class EntityBase { }
public class MyEntity : EntityBase { }
public abstract class RepositoryBase<T> where T : EntityBase { }
public class MyEntityRepository : RepositoryBase<MyEntity> { }
And now the casting line:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;
So, can anyone explain how is this invalid? And, I you are not in the mood to explain - is there a line of code I can use to actually do this cast?
RepositoryBase<EntityBase> is not a base class of MyEntityRepository. You're looking for generic variance which exists in C# to a limited extent, but wouldn't apply here.
Suppose your RepositoryBase<T> class had a method like this:
void Add(T entity) { ... }
Now consider:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;
baseRepo.Add(new OtherEntity(...));
Now you've added a different kind of entity to a MyEntityRepository... and that can't be right.
Basically, generic variance is only safe in certain situations. In particular generic covariance (which is what you're describing here) is only safe when you only ever get values "out" of the API; generic contravariance (which works the other way round) is only safe when you only ever put values "into" the API (e.g. a general comparison which can compare any two shapes by area can be considered as a comparison of squares).
In C# 4 this is available for generic interfaces and generic delegates, not classes - and only with reference types. See MSDN for further information, read <plug>read C# in Depth, 2nd edition, chapter 13</plug> or Eric Lippert's blog series on the topic. Also, I gave a one hour talk about this at NDC in July 2010 - the video is available here.
Whenever someone asks this question, I try to take their example and translate it to something using more well-known classes that is obviously illegal (this is what Jon Skeet has done in his answer; but I'm taking it a step further by performing this translation).
Let's replace MyEntityRepository with MyStringList, like this:
class MyStringList : List<string> { }
Now, you seem to want MyEntityRepository to be castable to RepositoryBase<EntityBase>, the reasoning being that this ought to be possible since MyEntity derives from EntityBase.
But string derives from object, doesn't it? So by this logic we should be able to cast a MyStringList to a List<object>.
Let's see what can happen if we allow that...
var strings = new MyStringList();
strings.Add("Hello");
strings.Add("Goodbye");
var objects = (List<object>)strings;
objects.Add(new Random());
foreach (string s in strings)
{
Console.WriteLine("Length of string: {0}", s.Length);
}
Uh-oh. Suddenly we're enumerating over a List<string> and we come upon a Random object. That's not good.
Hopefully this makes the issue a bit easier to understand.
This requires covariance or contravariance, whose support is limited in .Net, and cannot be used on abstract classes. You can use variance on interfaces though, so a possible solution to your problem is to create an IRepository which you use in place of the abstract class.
public interface IRepository<out T> where T : EntityBase { //or "in" depending on the items.
}
public abstract class RepositoryBase<T> : IRepository<T> where T : EntityBase {
}
public class MyEntityRepository : RepositoryBase<MyEntity> {
}
...
IRepository<EntityBase> baseRepo = (IRepository<EntityBase>)myEntityRepo;

BaseClass cannot implement interface.variable because it does not have the matching return type

EDIT: SOLVED. I am an idiot sometimes. See my self-answer below...
I'm working on the following C# .Net4.5 code using interfaces and polymorphism
public interface IFile
{
List<string> Contents {get;set;}
}
public class File : IFile
{
public List<string> Contents {get;set;}
}
public interface ICommandFile : IFile
{
new List<ICommandFileLine> Contents {get;set;} /*ICommandFileLine is defined in the code as well, but I don't believe this is pertinent to the issue I am having.*/
}
public class CommandFile : File, ICommandFile
{
public new List<ICommandFileLine> Contents {get;set;}
}
When I try and do the above, the pre-compiler complains:
CommandFile does not implement interface memeber
"ICommandFile.Contents". 'File.Contents' cannot implement
"ICommandFile.Contents" because it does not have the matching return
type of "List[ICommandFileLine]"
Interface member "List ICommandFile.Contents" is not
implemented.
I don't understand. I'm using the new keyword, surely this should indicate I wish to encapsulate the base class and define the new variable type?
Try a generic interface - IFile<T>. Then ICommandFile implements IFile<CommandLine>. Get the idea?
An interface member implementation should not be marked 'new' )as you have in your CommandFile class), that would mean the member in the implementing class is unrelated to the member inherited from the interface.
I realised what I did wrong not 5 minutes after posting:
List[ICommandFile] Contents EQUALS!? No, {get;set;}!
Changed:
new List[ICommandFileLine] Contents = new ListICommandFileLine;
in my local code to:
new List[ICommandFileLine] Contents {get;set;}
How embarrassing...
I'm still relatively new to implementing interfaces and making these mistakes from time to time.

Compact syntax for static Create() method on generic class?

I have a pair of classes. These are literally copy/paste from my project:
public static class PageResult
{
public static PageResult<T> Create<T>(int totalCount, IList<T> items)
{
return new PageResult<T>()
{
TotalCount = totalCount,
Items = items,
};
}
}
public class PageResult<T>
{
public int TotalCount { get; set; }
public IList<T> Items { get; set; }
}
The reason I did this is so that I could use PageResult.Create(5, listOf5Items) as opposed to any other lengthier syntax. I didn't put the Create method in the PageResult(T) class because I'm pretty sure that requires me to type PageResult<int>(5, listOf5Numbers) instead, and that's an extra five characters...
But having two classes for it seems pretty lame. Is there a way I can get the more compact syntax without having a throwaway class just to store it?
As you already noted, you'd have to specify type parameters to even access the Create function, because that specific PageResult<T> class won't even exist until the JIT creates it when a method starts calling it. See Tuples for an instance of the .NET Framework itself doing just this pattern for the basically the same reason.
Note that another option is to make the PageResult class non-static, and inherit PageResult<T> : PageResult which will allow you to store a collection of PageResult objects without a type parameter. This can also be useful if you use an abstract PageResult
No. You could potentially make a VS snippet (or some other plugin/tool that could generate source code) to have some of the boilerplate typed out for you, but at the end of the day that code will need to be there.

C# generic factory method

Perhaps this is a simple newbie C# question, but so be it---it will be a fresh break from my other questions, which are so difficult that no one knows the answer to them. :)
Let's say I have a generic type in C#:
Thing<T>
And let's say I want to make a thing using a static factory method. In Java, this is no problem:
public static <T> Thing<T> createThing()
{
return flag ? new Thing<Integer>(5) : new Thing<String>("hello");
}
How do I do this in C#? Thanks.
If you want to return an instance of a templated class using one of many different template arguments, one way to do it is with an abstract base (or an interface):
abstract class UntypedThing { }
class Thing<T> : UntypedThing
{
public Thing(T t) { }
}
class Foo
{
public static UntypedThing createThing(bool flag)
{
if (flag)
return new Thing<int>(5);
else return new Thing<String>("hello");
}
}
The UntypedThing class would contain as much code as possible that does not rely on the template type. The Thing class would ideally only contain code that relies on the template type. The factory class Foo always returns the former.
You can in theory use reflection to build up the correct generic type, but it will be pretty useless to you as at some point you will need to upcast it to a less specific type.
public class ThingFactory {
public object Create(bool flag) {
Type outputType = null;
if(flag) {
outputType = typeof(string);
} else {
outputType = typeof(int);
}
return Activator.CreateInstance(typeof(Thing<>).MakeGenericType(outputType));
}
}
As you can see, the value of doing this is about zero as you will need to cast the return type to the type you want, meaning that the logic to determine it needs to live outside the Create method.
I would use Reinderien's method and have a non-generic base. This is the most sane and idiomatic approach.
Oh, the trouble I get myself in when I simply try to do something simple.
It turns out that C# 4 allows this sort of covariance---sort of. First, I have to make Thing an interface and specify the "out" generic parameter:
public interface Thing<out T> {...}
But if I do certain things, C# won't let me use covariance. For example, if I try to return T from the interface:
public interface Thing<out T>
{
public T GetT();
Even if I manage to get covariance with Thing, what do I do with it?
Thing<object> thing=createThing();
The compiler tells me that the type cannot be inferred from usage.
Let's say I say screw the whole T thing and make the factory method return Thing of type object:
public static Thing<object> createThing() {...}
Fine, but now where do I put it?
IList<Thing<object>> list=new List<Thing<object>>();
Thing<object> thing=createThing();
list.Add(thing);
Yes, I have to say that this is a list of Thing with T of type Object, because C# has no wildcard type.
If this were Java, I'd simply say:
public class Thing<T> {...}
public static <T> Thing<T> createThing() {...}
List<?> things=new ArrayList<Thing<?>>();
Thing<?> thing=createThing();
things.add(thing);
If I wanted extra safety by saying that T had to be of a special type, I'd say:
public static <T extends MyBaseType> Thing<T> createThing() {...}
List<? extends MyBaseType> things=new ArrayList<Thing<? extends MyBaseType>>();
Thing<? extends MyBaseType> thing=createThing();
things.add(thing);
Then I'd figure out what T is later, when I had more information.
This all seems to come down to incomplete generic covariance in C# coupled with the lack of C# generic wildcards. (I still maintain it isn't an erasure issue.)
So what do I do? The only simple thing to do seems to follow Reinderien's answer and split out a non-generic base class.
(I wonder if in this non-generic base class I could have object getValue() and then use covariance in the subclass to return T getValue()? Ack, I'm tired of this---I'll leave that for another day.)

Non-constructed generics as properties (eg. List<T>)

The Problem
It's something I came across a while back and was able to work around it somehow. But now it came back, feeding on my curiosity - and I'd love to have a definite answer.
Basically, I have a generic dgv BaseGridView<T> : DataGridView where T : class. Constructed types based on the BaseGridView (such as InvoiceGridView : BaseGridView<Invoice>) are later used in the application to display different business objects using the shared functionality provided by BaseGridView (like virtual mode, buttons, etc.).
It now became necessary to create a user control that references those constructed types to control some of the shared functionality (eg. filtering) from BaseGridView. I was therefore hoping to create a public property on the user control that would enable me to attach it to any BaseGridView in Designer/code: public BaseGridView<T> MyGridView { get; set; }. The trouble is, it doesn't work :-) When compiled, I get the following message:
The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)
Solutions?
I realise I could extract the shared functionality to an interface, mark BaseGridView as implementing that interface, and then refer to the created interface in my uesr control.
But I'm curious if there exists some arcane C# command/syntax that would help me achieve what I want - without polluting my solution with an interface I don't really need :-)
EDIT: For reference, I did try this innocent workaround: BaseGridView<object> MyGridView { get; set; }, and... it still isn't the answer: Cannot implicitly convert type 'InvoiceGridView' to 'BaseGridView<object>'.
Partial success (edit 2)
Ok, because covariance is only supported on interfaces, I admitted defeat and defined an interface (only showing some of it):
public interface IBaseGridView<out T> where T : class
{
bool ScrollTo(Predicate<T> criteria);
bool ScrollTo(T object);
}
I am now able to cast my beloved InvoiceGridView to an IBaseGridView<object> - which is awesome and I'm a happy boy again :-) However, the second ScrollTo is giving me trouble upon compilation:
Invalid variance: The type parameter 'T' must be contravariantly valid on 'GeParts.Controls.IBaseGridView.ScrollTo(T)'. 'T' is covariant.
I'm now having to modify the signature to ScrollTo(object o) - which isn't ideal but gets the job done. What suprised me was that the compiler complained about the second ScrollTo yet was happy with the first one. So it seems that one isn't allowed to pass instances of an out T, but using the type itself (eg. in Predicate<T>) is fine? Seems rather picky...
Since you wrote
But I'm curious if there exists some arcane C# command/syntax that would help me achieve what I want
I'd like to add that C# 4.0 makes it possible to substitute derived types for a base type using < out T > for covariance. So you could do
public BaseGridView<Object> MyGridView { get; set; }
So you get a well known type but you can return whatever BaseGridView you want. The only catch is unfortunately that covariance is only allowed on interfaces! :(
C# doesn't support generic properties to my knowledge. Your options are either to create generic methods or to make the generic type part of your class definition.
For example:
public BaseGridView<T> GetMyGridView<T>() { ... }
public void SetMyGridView<T>(T gridView) { ... }
or
class MyClass<T> {
public BaseGridView<T> MyGridView { get; set; }
}
The following would probably work:
public BaseGridView<T> MyGridView<T> { get; set; }
The problem with your original answer is that the type parameter has to appear on the method or class declaration, not just on the return value.
Note that the compiler cannot infer generic types from return values, so you'll be required to specify T in every call to MyGridView.
I just tried whipping together some code and it works fine for me:
public class A<T> where T : class
{
public virtual A<T> ARef
{
get { return default(A<T>); }
}
}
public class B : A<B>
{
public override A<B> ARef
{
get
{
return base.ARef;
}
}
}
Shouldn't it be like this:
public BaseGridView MyGridView { get; set; }
public BaseGridView<T> GetMyGridView<T> { return whatever; }
public void SetMyGridView<T>( BaseGridView<T> bgv) { whatever = bgv; }
??
Edited. Matthew is right, Properties may not be generic. You would have to use a getter/setter.

Categories

Resources