I have a worker class that does stuff with a collection of objects. I need each of those objects to have two properties, one has an unknown type and one has to be a number.
I wanted to use an interface so that I could have multiple item classes that allowed for other properties but were forced to have the PropA and PropB that the worker class requires.
This is the code I have so far, which seemed to be OK until I tried to use it. A list of MyItem is not allowed to be passed as a list of IItem even though MyItem implements IItem. This is where I got confused.
Also, if possible, it would be great if when instantiating the worker class I don't need to pass in the T, instead it would know what T is based on the type of PropA.
Can someone help get me sorted out?
Thanks!
public interface IItem<T>
{
T PropA { get; set; }
decimal PropB { get; set; }
}
public class MyItem : IItem<string>
{
public string PropA { get; set; }
public decimal PropB { get; set; }
}
public class WorkerClass<T>
{
private List<T> _list;
public WorkerClass(IEnumerable<IItem<T>> items)
{
doStuff(items);
}
public T ReturnAnItem()
{
return _list[0];
}
private void doStuff(IEnumerable<IItem<T>> items)
{
foreach (IItem<T> item in items)
{
_list.Add(item.PropA);
}
}
}
public void usage()
{
IEnumerable<MyItem> list= GetItems();
var worker = new WorkerClass<string>(list);//Not Allowed
}
You can make this work if you supply the interface directly instead of the concrete type. It just isn't able to do the implicit conversion for you:
IEnumerable<IItem<string>> items = GetItems().Cast<IItem<string>>();
var worker = new WorkerClass<string>(items);
On an aside: Your original code would actually work in C# 4, which supports covariance on IEnumerable<T>. But previous versions of C# don't, which is why you get the error.
Related
I have a third party DLL that returns objects like Customers, Orders, etc. I'll call them Your Entities. They do have a common IYourEntity interface so I can use that as a source constraint.
I want to create a generic conversion extension method to convert all these different third party entities to My Entities with some streamlined and more maintainable code.
....but I can't figure out how to make a generic extension method that will call the concrete extension method for the specific conversion of each class.
Putting some of the main aspects of my code below, but you can get a full fiddle to play with here.
Yes, I'm probably showing I'm a bit clueless on how to do this and maybe trying to combine different concepts. Any pointers much appreciated as I've been beating my head for a couple of days now and need a life line :)
public interface IYourEntity
{
int Id
{
get;
set;
}
}
public interface IConvertToMyEntity<TYourEntity, TMyEntity>
where TYourEntity : class, IYourEntity, new()
where TMyEntity : class, IMyEntity, new()
{
TMyEntity ToMyEntity(TYourEntity yourEntity);
}
public static class ExtensionMethods
{
private static IMyEntity ToMyEntity(this IYourEntity yourEntity)
{
return new MyEntity1();
}
public static List<IMyEntity> ToMyEntityList(this List<IYourEntity> lstYourEntities)
{
return lstYourEntities.ConvertAll(q => q.ToMyEntity());
}
}
public class YourEntity1 : IYourEntity, IConvertToMyEntity<YourEntity1, MyEntity1>
{
public int Id
{
get;
set;
}
public string YourEntityName
{
get;
set;
}
public MyEntity1 ToMyEntity(YourEntity1 yourEntity)
{
return new MyEntity1()
{Id = yourEntity.Id, MyEntityName = yourEntity.YourEntityName, CreatedOn = DateTime.UtcNow};
}
public List<MyEntity1> ToMyEntityList(List<YourEntity1> lstYourEntities)
{
return lstYourEntities.ConvertAll(q => ToMyEntity(q));
}
}
Since the classes implementing IYourEntity are from a third party and not under your control, you can't implement an own IConvertToMyEntity<T1, T2> interface upon these.
One way you can handle it is by overloads of such conversion (extension) methods.
There's no need for any generic T type arguments; the common IYourEntity interface suffices.
Suppose you have 3 classes implementing the IYourEntity interface;
e.g. YourCustomer, YourOrder and YourProduct.
These need to be converted to IMyEntity instances, of which you might have different concrete implementations;
e.g. a general MyEntity and a specific MyProduct.
For the conversion you set up an extension method targeting IYourEntity.
This extension method will be called to convert an IYourEntity to an IMyEntity in case a more specific overload of this extension method does not exist.
public static IMyEntity ToMyEntity(this IYourEntity target)
{
return new MyEntity { Id = target.Id, EntityName = "Fallback name" };
}
For the entities that require a custom conversion, you set up overloads of this extension method targeting those specific source class types.
Below are such ones for YourOrder and YourProduct (but not for YourCustomer).
public static IMyEntity ToMyEntity(this YourOrder target)
{
return new MyEntity { Id = target.Id, EntityName = target.OrderName.ToUpper() };
}
public static IMyEntity ToMyEntity(this YourProduct target)
{
return new MyProduct { Id = target.Id * 100, EntityName = target.ProductName };
}
Next, define the extension method to convert the list of IYourEntity instances to a list of IMyEntity instances. In the code below, the inbetween cast to dynamic enables that the appropriate ToMyEntity overload will be called.
Note that the ToMyEntity methods don't have to be extension methods, but it might be convenient to have these in place in case you need to convert a single instance instead of a list.
public static List<IMyEntity> ToMyEntities(this List<IYourEntity> target)
{
var myEntities = new List<IMyEntity>();
foreach (var yourEntity in target)
{
var myEntity = Extensions.ToMyEntity((dynamic)yourEntity);
myEntities.Add(myEntity);
}
return myEntities;
}
An example - .net fiddle
var yourEntities = new List<IYourEntity>()
{
new YourCustomer() { Id = 1 },
new YourOrder() { Id = 2, OrderName = "Order-2"},
new YourProduct() { Id = 3, ProductName = "Product-3"}
};
var myEnties = yourEntities.ToMyEntities();
myEnties.ForEach(o => Console.WriteLine("{0} - {1} ({2})",
o.Id, o.EntityName, o.GetType().Name
));
The output of the example above looks like below.
Notice how the YourCustomer instance was handled by the general IYourEntity conversion, whereas the YourOrder and YourProduct instances got a specific treatment.
1 - Fallback name (MyEntity)
2 - ORDER-2 (MyEntity)
300 - Product-3 (MyProduct)
You can change your extension method to this:
private static IMyEntity ToMyEntity(this IYourEntity yourEntity)
{
if (yourEntity is IConvertToMyEntity<IYourEntity, IMyEntity> convertible)
return convertible.ToMyEntity;
return new MyEntity1();
}
This will not work in most cases unless you also make your interface co- and contra-variant:
public interface IConvertToMyEntity<in TYourEntity, out TMyEntity>
where TYourEntity : class, IYourEntity, new()
where TMyEntity : class, IMyEntity, new()
{
TMyEntity ToMyEntity(TYourEntity yourEntity);
}
It is still not completely clear to me how you can make a third party class implements IConvertToMyEntity that easily. Assuming you did this only to show us your actual goal, you should be very careful with what you are trying to accomplish in the Main.
If you use a List<IYourEntity>, you can only use methods and properties defined in the interface, unless you know what you are doing with specific cast. The need for List<IYourEntity> or List<IMyEntity> limits a lot the implementation of a custom mapper between My classes and Your classes. Here a possible solution:
As I said, I did not change Your classes:
public interface IYourEntity
{
int Id
{
get;
set;
}
}
public class YourEntity1 : IYourEntity
{
public int Id
{
get;
set;
}
public string YourEntityName
{
get;
set;
}
}
Also My classes are very simple and do not contain any logic for the mapping. This is a debatable choice, but I generally prefer to keep conversion logic separated from the classes involved. This helps to maintain clean your code in case you have several conversion functions for the same pair of classes. By the way, here they are:
public interface IMyEntity
{
int Id
{
get;
set;
}
DateTime CreatedOn
{
get;
set;
}
}
public class MyEntity1 : IMyEntity
{
public int Id
{
get;
set;
}
public string MyEntityName
{
get;
set;
}
public DateTime CreatedOn
{
get;
set;
}
}
And this is how I designed the custom converter
public interface IMyEntityConverter
{
IMyEntity Convert(IYourEntity yourEntity);
}
public class MyEntity1Converter : IMyEntityConverter
{
public IMyEntity Convert(IYourEntity yourEntity)
{
var castedYourEntity = yourEntity as YourEntity1;
return new MyEntity1()
{
Id = castedYourEntity.Id,
MyEntityName = castedYourEntity.YourEntityName,
CreatedOn = DateTime.UtcNow
};
}
}
It is clear the lack of genericity, but you cannot do otherwise if you need an extension method on a List of generic My and Your classes. Also tried with covariant and contravariant interfaces but C# does not let you use them with this implementation.
Now the core of the solution: you need something that binds Your class to the My class with a custom converter, and all of this should be as more transparent as possible.
public class EntityAdapter<YourType, MyType>
where YourType : IYourEntity
where MyType : IMyEntity
{
protected YourType wrappedEntity;
protected IMyEntityConverter converter;
public EntityAdapter(YourType wrappedEntity, IMyEntityConverter converter)
{
this.wrappedEntity = wrappedEntity;
this.converter = converter;
}
public static implicit operator YourType(EntityAdapter<YourType, MyType> entityAdapter) => entityAdapter.wrappedEntity;
public static explicit operator MyType(EntityAdapter<YourType, MyType> entityAdapter) =>
(MyType) entityAdapter.converter.Convert(entityAdapter.wrappedEntity);
public MyType CastToMyEntityType()
{
return (MyType) this;
}
}
The pseudo-transparency here is given by the implicit cast to Your class. The advantage is that you can cast this EntityAdapter to an instance of a My class by calling CastToMyEntityType or the explicit operator overload.
The painful part is with the extension methods:
public static class EntityAdapterExtensions
{
public static List<IMyEntity> ToIMyEntityList(this List<EntityAdapter<IYourEntity, IMyEntity>> lstEntityAdapters)
{
return lstEntityAdapters.ConvertAll(e => e.CastToMyEntityType());
}
public static List<EntityAdapter<IYourEntity, IMyEntity>> ToEntityAdapterList(this List<IYourEntity> lstYourEntities)
{
return lstYourEntities.Select(e =>
{
switch (e)
{
case YourEntity1 yourEntity1:
return new EntityAdapter<IYourEntity, IMyEntity>(yourEntity1, new MyEntity1Converter());
default:
throw new NotSupportedException("You forgot to map " + e.GetType());
}
}).ToList();
}
}
The first one is pretty straightforward to understand, but the second one is definitely something that require maintenance. I gave up on generics for the reasons already explained, so the only thing left to do is to create the EntityAdapters starting from the actual entity types.
Here is the fiddle
This may be a little controversial but maybe a different way is better?
Firstly, and this is more for my sake, I would suggest more easily understandable terminology so instead of 'your' and 'my' I would use 'source' and 'dest'.
Secondly I wonder if the generics route is necessary? I'm assuming (and I may be wrong) that for each of the classes you have coming from your third-party assembly, you have a specific class for it to be converted to. So maybe this could be achieved much more easily with a constructor override in your destination class.
// third party class example
public class SourceClass
{
public int Id { get; set; }
public string Name { get; set; }
}
// the destination class in your project
public class DestClass
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedOn { get; set; }
// default constructor
public DestClass()
{
}
// conversion constructor
public DestClass(SourceClass source)
{
Id = source.Id;
Name = source.Name;
CreatedOn = DateTime.UtcNow;
}
}
This way you convert a single instance using:
// source being an instance of the third-party class
DestClass myInstance = new DestClass(source);
And you can convert a list with LINQ:
// source list is IList<SourceClass>
IList<DestClass> myList = sourceList.Select(s => new DestClass(s)).ToList();
If you wanted to you could implement extensions for your conversions. This again would not be generic as you'll need one for each class pairing but as it's an alternative to writing a converter class for each, it will be overall less code.
public static class SourceClassExtensions
{
public static DestClass ToDest(this SourceClass source)
=> new DestClass(source);
public static IList<DestClass> ToDest(this IList<SourceClass> source)
=> source.Select(s => new DestClass(s)).ToList();
}
If you still want something generic then you'll want a converter for each class pair, implementing a suitable interface. Then I'd recommend a converter factory class where you'll need to register the specific converters either into a dictionary in the class or via dependency injection. I can go into this further if you'd prefer but I think it would be more complicated.
sorry for writing here its not an actual answer,
there is no option for generically to do this
you have to write for every entity
public interface IConvertToMyEntity<TYourEntity, TMyEntity>
where TYourEntity : class, IYourEntity, new()
where TMyEntity : class, IMyEntity, new()
{
TMyEntity ToMyEntity(TYourEntity yourEntity);
}
I saw this code from your question.
It depends on what you want to do after transformation
you should use data mapper
public class MapProfile : Profile
{
public MapProfile()
{
CreateMap<TYourEntity , TMyEntity >();
CreateMap<TMyEntity , TYourEntity >();
}
}
First of all apologize for long post nevertheless i wanted to highlight problem exactly and to be most readable and understandably. I am developing architecture of my program which will be responsible for files/databases data gather and face some architecture issues so far. All information step by step down below.
Let's consider following code below:
public interface IWatchService<TEntity> where TEntity : IEntity
{
IList<TEntity> MatchingEntries { get; set; }
}
public interface IWatchServiceDatabase<TEntity> : IWatchService<TEntity> where TEntity : IDatabaseEntity
{ }
public interface IWatchServiceFiles<TEntity> : IWatchService<TEntity> where TEntity : IFileEntity
{ }
class Database : IWatchServiceDatabase<DatabaseQuery>
{
public IList<DatabaseQuery> MatchingEntries { get; set; }
}
class Files : IWatchServiceFiles<CsvFile>
{
public IList<CsvFile> MatchingEntries { get; set; }
}
class Consumer
{
public IWatchService<IEntity> WatchService { get; set; }
public Consumer(IWatchService<IEntity> watchService)
{
WatchService = watchService;
var newList = WatchService.MatchingEntries;
}
public void AddNewEntries(IEntity entity) => WatchService.MatchingEntries.Add(entity);
}
class Program
{
static void Main(string[] args)
{
IWatchServiceDatabase<DatabaseQuery> db = new Database();
IWatchServiceFiles<CsvFile> filesCsv = new Files();
var dbConsumer = new Consumer(db); //cannot convert from 'IWatchServiceDatabase<DatabaseQuery>' to 'IWatchService<IEntity>'
var filesCsvConsumer = new Consumer(filesCsv); //cannot convert from 'IWatchServiceFiles<CsvFile>' to 'IWatchService<IEntity>'
dbConsumer.AddNewEntries(new DatabaseQuery());
dbConsumer.AddNewEntries(new CsvFile()); //illegal cause it's not FileConsumer !!!
filesCsvConsumer.AddNewEntries(new CsvFile());
filesCsvConsumer.AddNewEntries(new DatabaseQuery()); //illegal cause it's not DbConsumer !!!
}
}
public interface IEntity { }
public interface IFileEntity : IEntity
{
int Id { get; set; }
string Name { get; set; }
}
public interface IDatabaseEntity : IEntity { }
public class CsvFile : IFileEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class XmlFile : IFileEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DatabaseQuery : IDatabaseEntity { }
We have two errors there:
var dbConsumer = new Consumer(db);
var filesCsvConsumer = new Consumer(filesCsv);
Errors:
cannot convert from 'IWatchServiceDatabase' to 'IWatchService'
cannot convert from 'IWatchServiceFiles' to 'IWatchService'
This seems to be understandable because otherwise "we would be able" to add CsvFile or XmlFile to dbConsumer where generic IDatabaseEntity is expected and CsvFile and XmlFile are in fact IFileEntity and from the other hand DatabaseQuery to filesConsumer which expects IFileEntity and DatabaseQuery is IDatabaseEntity
//Database related
dbConsumer.AddNewEntries(new DatabaseQuery());
dbConsumer.AddNewEntries(new CsvFile()); //illegal cause it's not FileConsumer !!!
//Files related
filesCsvConsumer.AddNewEntries(new CsvFile());
filesCsvConsumer.AddNewEntries(new DatabaseQuery()); //illegal cause it's not DbConsumer !!!
From my understanding this is the clue why compiler raise those errors and which is fine. Therefore I've decided to overcome it in this way:
public interface IWatchService<out TEntity> where TEntity : IEntity
{
IEnumerable<TEntity> MatchingEntries { get; }
}
As can be seen i marked generic parameter out and changed IList to IEnumerable because IEnumerable can be only foreached. Without possibility to modify the list.
Now having this there is no possibility to modify MatchingEntries e.g Add() on therefore we are now not able to add e.g CsvFile (IFileEntity) where IDatabaseEntity is expected and vice versa DatabaseQuery (IDatabaseEntity) where IFileEntity is expected. Fine and understandably.
At the end i have two main questions:
What is the benefit to have this: IEnumerable MatchingEntries { get; } since it's {get;} it cannot be initialized or populated with values therefore i would always get empty list when calling that property. Or i am in wrong? Can somebody explain showing based on my code what can be done with it?
Let's imagine i want to have possibility to Add items to this MatchingEntries list and in Consumer class i want still to be able to pass in ctor either Database or Files related classes based on interfaces. How this can be accomplished? Please also show an example based on current code.
Many thanks for your support and hope someone benefit from it as i saw a lot of confusions related to that topic.
First question:
What is the benefit to have this: IEnumerable<T> MatchingEntries { get; } since it's {get;} it cannot be initialized or populated with values therefore I would always get empty list when calling that property. Or I am in wrong? Can somebody explain showing based on my code what can be done with it?
I am confused by the question. The interface says that a class that implements that interface must have a getter of this name and type. It says nothing at all about the contents of that sequence:
interface IFoo<out T>
{
IEnumerable<T> Bar { get; }
}
Now we can implement that interface however we want:
class TigerFoo : IFoo<Tiger>
{
public IEnumerable<Tiger> Bar
{
get
{
return new List<Tiger>() { new Tiger("Tony"), new Tiger("Terry") };
}
}
}
So why you think the returned sequence must be empty, I do not understand.
Similarly, nothing is stopping you from making a class that implements a setter:
class GiraffeFoo : IFoo<Giraffe>
{
public IEnumerable<Giraffe> Bar { get; set; }
}
…
GiraffeFoo gf = new GiraffeFoo();
List<Giraffe> giraffes = new List<Giraffe>() { new Giraffe("Gerry") };
gf.Bar = giraffes;
Nothing stops you from changing the contents of the list:
class TurtleFoo : IFoo<Turtle>
{
private List<Turtle> turtles = new List<Turtle>();
public IEnumerable<Turtle> Bar => turtles;
public void AddATurtle() => turtles.Add(new Turtle("Tommy"));
}
It is a mystery to me why you think you cannot do any of these things. You want to add a member to the collection? Write a method that adds a member to the collection. You just can't put it in the interface if you wan the interface to be covariant. But the interface tells you what services you must provide, not what services you must not provide! I do not understand why you think that an interface tells you what a class cannot do.
Since T is marked as out, you can now use any of these covariantly:
IFoo<Animal> ia1 = new TigerFoo();
IFoo<Animal> ia2 = new GiraffeFoo();
IFoo<Animal> ia3 = new TurtleFoo();
Of course you don't get to use the methods of the class once it is in an interface, but you never get to use the methods of a class once something is in an interface.
Second question:
Let's imagine I want to have possibility to Add items to this MatchingEntries list and in Consumer class i want still to be able to pass in ctor either Database or Files related classes based on interfaces. How this can be accomplished? Please also show an example based on current code.
Just write code that does that. I don't understand what the question is asking. Please clarify the question.
Is it possible to add different type of generic objects to a list?. As below.
public class ValuePair<T>
{
public string Name { get; set;}
public T Value { get; set;
}
and let say I have all these objects...
ValuePair<string> data1 = new ValuePair<string>();
ValuePair<double> data2 = new ValuePair<double>();
ValuePair<int> data3 = new ValuePair<int>();
I would like to hold these objects in a generic list.such as
List<ValuePair> list = new List<ValuePair>();
list.Add(data1);
list.Add(data2);
list.Add(data3);
Is it possible?
In general, you'd have to either use a List<object> or create a non-generic base class, e.g.
public abstract class ValuePair
{
public string Name { get; set;}
public abstract object RawValue { get; }
}
public class ValuePair<T> : ValuePair
{
public T Value { get; set; }
public object RawValue { get { return Value; } }
}
Then you can have a List<ValuePair>.
Now, there is one exception to this: covariant/contravariant types in C# 4. For example, you can write:
var streamSequenceList = new List<IEnumerable<Stream>>();
IEnumerable<MemoryStream> memoryStreams = null; // For simplicity
IEnumerable<NetworkStream> networkStreams = null; // For simplicity
IEnumerable<Stream> streams = null; // For simplicity
streamSequenceList.Add(memoryStreams);
streamSequenceList.Add(networkStreams);
streamSequenceList.Add(streams);
This isn't applicable in your case because:
You're using a generic class, not an interface
You couldn't change it into a generic covariant interface because you've got T going "in" and "out" of the API
You're using value types as type arguments, and those don't work with generic variable (so an IEnumerable<int> isn't an IEnumerable<object>)
Not unless you have a non-generic base-type ValuePair with ValuePair<T> : ValuePair (it would work for an interface too), or use List<object>. Actually, though, this works reasonably:
public abstract class ValuePair
{
public string Name { get; set; }
public object Value
{
get { return GetValue(); }
set { SetValue(value); }
}
protected abstract object GetValue();
protected abstract void SetValue(object value);
}
public class ValuePair<T> : ValuePair
{
protected override object GetValue() { return Value; }
protected override void SetValue(object value) { Value = (T)value; }
public new T Value { get; set; }
}
No, it is not possible. You could create, in your case, a base class ValuePair from which ValuePair<T> derives. Depends on your purposes.
it's not possible as far as I know.
the line:
List<ValuePair> list = new List<ValuePair>();
you wrote in your sample is not providing a concrete type for T and this is the issue, once you pass it, you can only add object of that specific type.
I have the following problem:
public interface IControlSingleContainer
{
ControlCollection Content { get; set; }
}
public interface IControlCollectionContainer
{
// I need to obtain a List of objects that implement IControlSingleContainer interface
List<IControlSingleContainer> Items { get; set; }
}
public class TabItem : IControlSingleContainer
{
public ControlCollection Content { get; set; }
}
public class TabbedContainer : IControlCollectionContainer
{
public List<TabItem> Items { get; set; } <- ERROR!
}
This code expect a List<IControlSingleContainer> in the property TabbedContainer.Items but I try to create classes with Items property that contains objects that implement IControlSingleContainer.
EDITED: Basically, the compilation error is the following:
'Cosmo.UI.Controls.TabbedContainer' does not implement interface member 'Cosmo.UI.Controls.IControlCollectionContainer.Items'. 'Cosmo.UI.Controls.TabbedContainer.Items' can not implement' Cosmo.UI.Controls.IControlCollectionContainer.Items' because it has the kind of matching return value of 'System.Collections.Generic.List <Cosmo.UI.Controls. IControlSingleContainer>'.
I explored a solution with generic interfaces but without any results...
Not entirely sure what you are trying to do here but you can't coerce a List<interface> into a List<concrete>. However, you can make your interface generic and add a constraint like this:
public interface IControlCollectionContainer<T> where T : IControlSingleContainer
{
List<T> Items { get; set; }
}
Now your class definition becomes this:
public class TabbedContainer : IControlCollectionContainer<TabItem>
{
public List<TabItem> Items { get; set; }
}
You're close,
public class TabbedContainer : IControlCollectionContainer
{
public TabbedContainer()
{
Items = new List<IControlSingleContainer>();
var t = new TabItem();
Items.Add(t);
}
public List<IControlSingleContainer> Items { get; set; }
}
This is one of the reasons explicit interface implementations exist depending on your use-case.
In your situation, you want your Items to be TabItem when working with that TabbedContainer directly. However, the interface requires Items to be a specific interface.
The trick is to declare TabbedContainer.Items and also IControlCollectionContainer at the same time, but reusing your TabItem class behind the scenes.
public class TabbedContainer : IControlCollectionContainer
{
public List<TabItem> Items { get; set; }
List<IControlSingleContainer> IControlCollectionContainer.Items
{
get
{
return // Your actual tab items
}
set
{
Items = //Whatever you need to do make sure you have actual
// TabItem objects
}
}
}
You'll need to update the sample above to actually handle setting/getting of interface's version of the items, but the main idea is to reuse your TabItem collection so they are always in sync.
What this actually does is when you are working with TabbedContainer and call Items, you will get a list of TabItem, but when working with your instance as an IControlCollectionContainer, your Items will return you the IControlCollectionContainer.Items instead.
Do note though that this can become quite a complex venture depending on how you are passing/modifying the instance of the container. It can be tricky to try and get these to sync if you are constantly modifying the items through TabbedContainer and IControlCollectionContainer declarations. Doing explicit implementations can sometimes help you take a step back and re-evaluate exactly what it is your end goal is and what types you declare on your properties.
For instance, if you aren't actually adding items to your interface's list, then why even use List? It could be better as an IEnumerable<T> or IReadOnlyCollection<T>.
Is it possible to add different type of generic objects to a list?. As below.
public class ValuePair<T>
{
public string Name { get; set;}
public T Value { get; set;
}
and let say I have all these objects...
ValuePair<string> data1 = new ValuePair<string>();
ValuePair<double> data2 = new ValuePair<double>();
ValuePair<int> data3 = new ValuePair<int>();
I would like to hold these objects in a generic list.such as
List<ValuePair> list = new List<ValuePair>();
list.Add(data1);
list.Add(data2);
list.Add(data3);
Is it possible?
In general, you'd have to either use a List<object> or create a non-generic base class, e.g.
public abstract class ValuePair
{
public string Name { get; set;}
public abstract object RawValue { get; }
}
public class ValuePair<T> : ValuePair
{
public T Value { get; set; }
public object RawValue { get { return Value; } }
}
Then you can have a List<ValuePair>.
Now, there is one exception to this: covariant/contravariant types in C# 4. For example, you can write:
var streamSequenceList = new List<IEnumerable<Stream>>();
IEnumerable<MemoryStream> memoryStreams = null; // For simplicity
IEnumerable<NetworkStream> networkStreams = null; // For simplicity
IEnumerable<Stream> streams = null; // For simplicity
streamSequenceList.Add(memoryStreams);
streamSequenceList.Add(networkStreams);
streamSequenceList.Add(streams);
This isn't applicable in your case because:
You're using a generic class, not an interface
You couldn't change it into a generic covariant interface because you've got T going "in" and "out" of the API
You're using value types as type arguments, and those don't work with generic variable (so an IEnumerable<int> isn't an IEnumerable<object>)
Not unless you have a non-generic base-type ValuePair with ValuePair<T> : ValuePair (it would work for an interface too), or use List<object>. Actually, though, this works reasonably:
public abstract class ValuePair
{
public string Name { get; set; }
public object Value
{
get { return GetValue(); }
set { SetValue(value); }
}
protected abstract object GetValue();
protected abstract void SetValue(object value);
}
public class ValuePair<T> : ValuePair
{
protected override object GetValue() { return Value; }
protected override void SetValue(object value) { Value = (T)value; }
public new T Value { get; set; }
}
No, it is not possible. You could create, in your case, a base class ValuePair from which ValuePair<T> derives. Depends on your purposes.
it's not possible as far as I know.
the line:
List<ValuePair> list = new List<ValuePair>();
you wrote in your sample is not providing a concrete type for T and this is the issue, once you pass it, you can only add object of that specific type.