I have two different types of item: Picture and Video. In order to manage them, I have created an interface and two classes which implement it
public interface Item{
}
public class ItemPicture: Item{
}
public class ItemVideo: Item {
}
Now I have some classes to manage that items, that inherit from a manager interface
public interface ItemManager<T>{
IList<T> FGetByGroup(string idgroup);
}
public class ItemManagerPictures : IItemManager<ItemPicture>{
public IList<ItemFoto> FGetByGroup(string idgroup){
...
}
}
public class ItemManagerVideos: IItemManager<ItemVideo>{
public IList<ItemVideo> FGetByGroup(string idgroup){
...
}
}
In order to have a factory method which creates the appropriate object, I have created this class
public class ItemManagerCreator
{
public static IItemManager<Item> MakeItemManager(string type)
{
IItemManager<Item> objMgr = null;
switch (type)
{
case "Picture":
objMgr = (IItemManager<Item>)new ItemManagerPictures();
break;
case "Video":
objMgr = (IItemManager<Item>)new ItemManagerVideos();
break;
default:
break;
}
return objMgr ;
}
}
From the controller I want to do this
var type="Picture";
IItemManager<Item> itemMgr = ItemManagerCreator.MakeItemManager(type);
var itemList = itemMgr.FGetByGroup(string idgroup);
But I get this casting error
Can't convert from type '...ItemManagerPictures' to type '...IItemManager`1[...Item]'.
which makes me think I'm doing something wrong, but after 1000 turns, I think the problem is with the factory method and generics, which is not well designed.
I'm new to design patterns, and maybe I'm not applying the right one.
Thanks in advance
It doesn't work as is, because ItemManagerPictures and ItemManagerVideos are indeed not convertible to IItemManager<Item>, check covariance and contravariance to see why.
In this case, because you actually do not need to use IList as return type of FGetByGroup (as figured out in comments), you can make it work by using some interface which is covariant in generic type T (such as IEnumerable<T> or IReadOnlyList<T>) and then declare your type T as covariant too:
public interface IItemManager<out T>{
IReadOnlyList<T> FGetByGroup(string idgroup);
}
public class ItemManagerPictures : IItemManager<ItemPicture>{
public IReadOnlyList<ItemPicture> FGetByGroup(string idgroup) {
return null;
}
}
Now, ItemManagerPictures is assignable to IItemManager<Item> so your code will work without exceptions.
From your code you seem to know beforehand what type of ItemManager you want, so you can do something like this :
public class ItemManagerCreator
{
public static IItemManager<T> MakeItemManager<T>() where T : Item
{
if (typeof(T) == typeof(ItemPicture))
{
return (IItemManager<T>)new ItemManagerPictures();
}
if (typeof(T) == typeof(ItemVideo))
{
return (IItemManager<T>)new ItemManagerVideos();
}
throw new InvalidOperationException();
}
}
And for use :
string groupId = string.Empty;
dynamic itemManager = null;
I want an IItemManager for pictures
itemManager = ItemManagerCreator.MakeItemManager<ItemPicture>();
IList<ItemPicture> pictures = itemManager.FGetByGroup(groupId);
Then I want an IItemManager for videos
itemManager = ItemManagerCreator.MakeItemManager<ItemVideo>();
IList<ItemVideo> videos = itemManager.FGetByGroup(groupId);
Related
I have an ASP.NET MVC (Not Core) project where I have run into some problems, and I think finally getting around to learning how to properly use generics could be a solution to my problems.
My case is that I have a SQL connection, that returns data and depending on the result of one field in the SQL, I want to use two different models. The models have a lot of properties in common so I thought the best practice would be to create a method that selects which of the models to create, fill in the differences, return the model and then continue to fill in the "common" properties.
I have tried to read a little on Generics but I am quite new to this so I haven't made any big strides.
My code example looks like this:
public ResultVM MainClass()
{
var resultData = new ResultVM();
// ... SQL returns data
while (reader.Read())
{
resultData.Add(reader);
}
return resultData;
}
public object CreateObject(SqlDataReader indata)
{
if((indata["Associate"].ToString()) == "0")
{
var individual = new Individual();
}
else
{
var group = new Group();
}
return object;
}
How can I dynamically (depending on the value of Associate field) create an individual or a group?
I suggest working directly with System.Type in your case. Here can be multiple more elegant solutions of your problem, depending of what you actually need:
indata.GetFieldType(int ordinal) will return the .NET type of your field
Serialize data with type handling, then you can simply get type after non generic deserialization. For example:
var result = JsonConvert.DeserializeObject(dataJson);
result will have Type of your actual object type. You can check it writing result.GetType() and create an object of this type. For more advanced use see Activator.CreateInstance(...)
For the most cases using interface is the best way:
interface ISomething
{
// ...
}
class Individual : ISomething
{
// ...
}
class Group : ISomething
{
// ...
}
Then you cat build your non generic method this way:
public ISomething CreateObject(SqlDataReader indata)
{
if((indata["Associate"].ToString()) == "0")
return new Individual();
else
return new Group();
}
Your generic object creation may look something like this:
public T CreateObject<T>(SqlDataReader indata)
{
var o = new T();
return o;
}
Where T is type, that you pass outside this method: CreateObject<YourType>(indata)
T can be any Type you want including Interfaces. Also working with generics you may want check types by using is keyword, but I recommend using interfaces and avoid is checks at all.
Example:
if(yourObject is YourType)
{
(yourObject as YourType).SomeSpecificToYourTypeMethod();
// ...
}
What about implementing an interface with all the common properties?
Something like
interface IInterface
{
string CommonProperty1 { get; set; }
string CommonProperty2 { get; set; }
}
class Individual : IInterface
{
// ...
}
class Group : IInterface
{
// ...
}
public IInterface CreateObject(SqlDataReader indata)
{
if((indata["Associate"].ToString()) == "0")
{
var individual = new Individual();
// ...
return individual;
}
else
{
var group = new Group();
// ...
return group;
}
}
I want to create a structure to store data consumed from a Web Service with the followind specs:
Response:
Field 1 - InstructionType: Can be 1 (PreferredDay), 2 (SVP), 3 (Neighbour)
Field 2: Some variable data. Its type depends on Field 1. So if:
Field 1 == 1 then Field 2 type will be of DateTime (dd.MM.yyyy)
Field 1 == 2 then Field 2 type will be of type string.
Field 1 == 3 then Field 2 type will be of type string
So, I started up with the following enum:
public enum InstructionType
{
None = 0,
PreferredDay = 1,
ServicePoint = 2,
Neighbour = 3
}
And the generic class:
public abstract class Instruction<T>
{
public InstructionType Type { get; private set; }
public T Data { get; private set; }
public Instruction(InstructionType type, T data)
{
this.Type = type;
this.Data = data;
}
}
and concrete classes:
public class PreferredDayInstruction : Instruction<DateTime>
{
public PreferredDayInstruction(DateTime data)
: base (InstructionType.PreferredDay, data) {}
}
public class ServicePointInstruction: Instruction<string>
{
public ServicePointInstruction(string data)
: base (InstructionType.ServicePoint, data) {}
}
public class NeughbourInstruction: Instruction<string>
{
public NeughbourInstruction(string data)
: base (InstructionType.Neighbour, data) {}
}
When parsing web service's response created a public function:
public Instruction DeliveryInstruction() <---Compiler error here "Instruction"
{
if (resultFromWebservice.Field1 == 1)
return new PreferredDayInstruction((DateTime)Field2);
if (resultFromWebservice.Field1 == 2)
return new ServicePointInstruction(Field2);
if (resultFromWebservice.Field1 == 3)
return new NeighbourInstruction(Field2);
}
and here is the problem. Can't return objects of generic type.
Tried with with Interface, factories, and other stuff, but allways with the same problem. So, is there any way to archieve this? maybe it's not possible or maybe is so easy I can't see now. Thanks in advance.
UPDATE:
Compiler error on BOLD Instruction
Error 1 Using the generic type 'NAMESPACE.Instruction' requires '1' type arguments
I forgot..I'm using .NET 3.5
It looks like you may be starting off with an intent to use generics rather than using them because you've identified a need. Often (not always) when that gets difficult it's because it didn't actually fit what you were trying to do.
What seems odd in this case is that you have both a generic type and an enum to indicate the type. This is likely to cause you a few problems.
First it looks like you're trying to create a one-size-fits all class to model different types of behaviors. That will start off confusing and get more confusing. Think of most classes that are part of the .NET framework, and imagine what would happen if they had properties like Field1 and Field2, and you couldn't tell from looking at them what they were for. And in one method they're used for one thing, but in a another case they mean something else.
Also, if you're trying to put different types of instructions in one class, that suggests that maybe you're going to try passing them all to one method, and that method figures out what to do, and maybe calls other methods. (I'm guessing that because of the enum. Perhaps you're going to handle the input differently depending on which value it contains.) That one method will get really hard to maintain.
I'd recommend waiting on generics until you're sure you need them. And if you have different types of instructions you're likely better off writing a different class for each one with the properties it needs and names that describe them, and writing methods for each of them to do what they need to do. If you need lots of classes, make lots of them.
It's very easy to fall into the trap of trying to solve problems that don't exist, like how do I write one class that covers a bunch of different needs. The answer usually that you don't need to. You'll get better results from writing more classes that each do fewer things.
Believe me that I tried to do my best to explain what was my problem and what I needed in order to solve it. In a nutshell, the question was quite simple. Is this possible or not? So, is there a way to return a common type for these 3 classes? Answer is no, as they don't share any root. They all derive from Instruction, but aren't compatible each other. That's what I learned from this experience.
As another example, lets take another .NET framework's generic type.
public class ListOfString : List<string> { }
public class ListOfInt : List<int> { }
public class ListOfDecimal : List<decimal> { }
And, in another place of the application, get a method who returns one of this List based on some logic:
public class Logic
{
public List<> GetList(Type t) <----This can't be done
{
if (t == typeof(string))
return new ListOfString();
if (t == typeof(int))
return new ListOfInt();
if (t == typeof(decimal))
return new ListOfDecimal();
else return null;
}
}
Please, keep in mind that this is just a stupid sample just to show what's the point of this post.
By the way, in the case of List the following can be done, because there is a non generic different version of IList:
public IList GetList(Type t)
{
....
}
But I can't think of a way to do this in my particular case.
Anyway, I finally followed another approach. I reallized that what I really wanted is to ensure Data property is valid. If it it's supposed to be a date there, ensure date is valid. Is it a string, ensure it has the right length or whatever rule it must follow.
So this is the final solution:
The enum:
public enum InstructionType
{
None = 0,
PreferredDay = 1,
ServicePoint = 2,
Neighbour = 3
}
The base class:
public abstract class Instruction
{
public InstructionType Type { get; private set; }
public string Data { get; private set; } <---Type String
public Instruction(InstructionType type, string data)
{
this.Type = type;
this.Data = IsValid(data) ? data : string.Empty;
}
public abstract bool IsValid(string data); <--the rule.
}
The concrete classes:
public class PreferredDayInstruction : Instruction
{
public PreferredDayInstruction(string date)
: base(InstructionType.PreferredDay, date) { }
public override bool IsValid(string data)
{
string[] formats = {"dd.MM.yyyy", "d.MM.yyyy",
"dd.MM.yy", "d.MM.yy"};
try
{
data = data.Replace('/', '.').Replace('-', '.');
var dateparts = data.Split('.');
DateTime date = new DateTime(Convert.ToInt32(dateparts[2]),
Convert.ToInt32(dateparts[1]),
Convert.ToInt32(dateparts[0]));
//DateTime.ParseExact(data, formats, null, System.Globalization.DateTimeStyles.AssumeLocal);
return true;
}
catch (Exception)
{
return false;
}
}
}
public class ServicePointInstruction : Instruction
{
public ServicePointInstruction(string data)
: base (InstructionType.ServicePoint, data) { }
public override bool IsValid(string data)
{
return ServicePointBarcodeValidator.Validate(data);
}
}
public class NeighbourInstruction : Instruction
{
public NeighbourInstruction(string data) :
base(InstructionType.Neighbour, data) { }
public override bool IsValid(string data)
{
return data.Length <= 70;
}
}
A factory class, who's responsability is to create and return the correct object based on the enum:
public static class DeliveryInstructionFactory
{
public static Instruction Create(int type, string data)
{
return Create((InstructionType)type, data);
}
public static Instruction Create(InstructionType type, string data)
{
switch (type)
{
case InstructionType.PreferredDay:
return new PreferredDayInstruction(data);
case InstructionType.ServicePoint:
return new ServicePointInstruction(data);
case InstructionType.Neighbour:
return new NeighbourInstruction(data);
default:
return null;
}
}
}
And finally, as now all of they share the same root, object can be created on webservice's response parser:
public Instruction DeliveryInstruction()
{
try
{
int instructionCode = int.Parse(observation.Substring(173,2));
string instructionData = observation.Substring(175, 10);
return DeliveryInstructionFactory.Create(instructionCode, instructionData); }
catch (Exception ex)
{
Log.Error("[ValidationBarcodeResponse] DeliveryInstructions aren't in the correct format", ex);
return null;
}
}
Hope this now fits on a Minimal, Complete, and Verifiable example
Just to clarify, I have this working using dynamic and MakeGenericType. But I cant help but think there is a better way to do this. What I am trying to do is create a "plug-in" loader, using Unity. I will just explain it as I post the code so you can get a sense for what I am doing.
First I'll just post the plug-in itself:
[RegisterAction("MyPlugin", typeof(bool), typeof(MyPlugin))]
public class MyPlugin: IStrategy<bool>
{
public IStrategyResult<bool> Execute(ISerializable info = null)
{
bool result;
try
{
// do stuff
result = true;
}
catch (Exception)
{
result = false;
}
return new StrategyResult<bool>
{
Value = result
};
}
}
Couple things to note here. First is the RegisterActionAttribute:
[AttributeUsage(AttributeTargets.Class)]
public sealed class RegisterActionAttribute : Attribute
{
public StrategyAction StrategyAction { get; }
public RegisterActionAttribute(string actionName, Type targetType, Type returnType, params string[] depdencies)
{
StrategyAction = new StrategyAction
{
Name = actionName,
StrategyType = targetType,
ResponseType = returnType,
Dependencies = depdencies
};
}
}
Then the interfaces:
public interface IStrategy<T>
{
IStrategyResult<T> Execute(ISerializable info = null);
}
public interface IStrategyResult<T>
{
bool IsValid { get; set; }
T Value { get; set; }
}
All fairly straight forward. The goal here is just to attach some meta-data to the class when it is loaded. The loading happens via unity using a wrapper that simply loads the assemblies in the bin directory using a file search pattern and adds it to a singleton class with a collection of StrategyActions. I don't need paste all the unity code here as I know it works and registers and resolves the assemblies.
So now to the meat of the question. I have a function on the singleton that executes actions. These are applied with Unity.Interception HandlerAttributes and passed a string like so (I can post the code for this but I didn't think it was relevant):
[ExecuteAction("MyPlugin")]
The handler calls the following execute function on the singleton class to "execute" functions that are registered (added to the collection).
public dynamic Execute(string action, params object[] parameters)
{
var strategyAction = _registeredActions.FirstOrDefault(a => a.Name == action);
if (strategyAction == null)
return null;
var type = typeof (IStrategy<>);
var generic = type.MakeGenericType(strategyAction.StrategyType);
var returnType = typeof (IStrategyResult<>);
var genericReturn = returnType.MakeGenericType(strategyAction.ResponseType);
var instance = UnityManager.Container.Resolve(generic, strategyAction.Name);
var method = instance.GetType().GetMethod("Execute");
return method.Invoke(instance, parameters);
}
This execute is wrapped in an enumerator call which returns a collection of results, which sorts to manage dependencies and what not (see below). These values are referenced by the caller using the Value property of ISTrategyResult{T} to do various things defined by other business rules.
public List<dynamic> ExecuteQueuedActions()
{
var results = new List<dynamic>();
var actions = _queuedActions.AsQueryable();
var sortedActions = TopologicalSort.Sort(actions, action => action.Dependencies, action => action.Name);
foreach(var strategyAction in sortedActions)
{
_queuedActions.Remove(strategyAction);
results.Add(Execute(strategyAction.Name));
}
return results;
}
Now mind you, this works, and I get the return type that is specified by the plugins RegisterAction attribute. As you can see I am capturing the Type of the plugin and the return type. I am using the "generic" variable to resolve the type with unity through the use of MakeGenericType, which works fine. I am also creating a generic representing the return type based on the type from the collection.
What I don't like here is having to use dynamic to return this value to a function. I can't figure out a way to return this as a IStrategyResult{T} because obviously the caller to "dynamic Execute(..." can not, at run-time, imply return type of the function. I mulled around with making the call to Execute with a MakeGenericMethod call as I actually have the expected type the StrategyAction. It would be cool if I could some how figure out away to return a strongly typed result of IStrategyResult{T} while determining the type of T during the call.
I do understand why I cannot do this with my current implementation I am just trying to find a way to wrap all this functionality without using dynamic. And was hoping somebody could provide some advice that might be useful. If that means wrapping this with other calls to non-generic classes or something like that, that would be fine as well if that is the only solution.
You need a more sweeping refactor than just figure out how to call your plugin.
There's no need for the [RegisterAction] attribute to hold targetType and returnType, these parameters to the attribute can easily get out of sync with code, making them a potential hole to fall into.
Then think from the other side of your setup: how do you consume the data, what do you do with your IStrategyResult<> - does it really have to be generic or there is a specific way you could encapsulate the type of results? I can't quite imagine a plugin system that returns "anything" to the host. The hint is really in your dynamic Execute(...) - your parameters and your result have both lost their strong typing, showing you that strong-typing the plugin is not helping with anything. Just use object or - better - make a StrategyResult class instead of the current interface and provide whatever properties are necessary there (I've added a few frivolous examples), such as:
public class StrategyResult{
public object Result{get;set;}
public Type ResultType {get;set;}
// frivolous examples
public bool IsError {get;set;}
public string ErrorMessage {get;set;}
// really off-the-wall example
public Func<StrategyHostContext,bool> ApplyResultToContext {get;set;}
public StrategyResult(){
}
public StrategyResult FromStrategy(IStrategy strategy){
return new StrategyResult{
ResultType = strategy.ResultType
}
}
public StrategyResult FromStrategyExecute(IStrategy strategy, ISerializable info = null){
var result = FromStrategy(strategy);
try{
strategy.Execute(info);
} catch (Exception x){
result.IsError = true;
result.ErrorMessage = x.Message;
}
}
}
Then your IStrategy becomes:
public interface IStrategy{
Type ResultType {get;}
void Initialize(SomeContextClassMaybe context);
StrategyResult Execute(ISerializable info = null);
}
You can also change your attribute to make it more efficient to load large plugins:
[AttributeUsage(AttributeTargets.Assembly)]
public sealed class AddinStrategyAttribute : Attribute
{
public Type StategyType {get; private set;}
public AddinStrategyAttribute(Type strategyType){
StrategyType = strategyType;
}
}
... and use the attribute like so:
[assembly:AddinStrategy(typeof(BoolStrategy))] // note it's outside the namespace
namespace MyNamespace{
public class BoolStrategy: IStrategy{
public Type ResultType { get{ return typeof(bool);}}
public void Initialize (SomeContextClassMaybe context){
}
public StrategyResult Execute(ISerializable info = null){
return StrategyResult.FromStrategyExecute(this,info);
}
}
}
Assuming that the caller of ExecuteActions does not have any knowledge about the T in any of the plugins or results and must work with dynamic or object anyway, then the following may work:
Infrastructure:
public interface IStrategy
{
IStrategyResult Execute(ISerializable info = null);
}
public interface IStrategyResult
{
bool IsValid { get; }
dynamic Value { get; }
}
public class StrategyResult<T> : IStrategyResult
{
public T Value { get; private set; }
public StrategyResult(T value) { this.Value = value; }
public bool IsValid { get { throw new NotImplementedException(); } }
dynamic IStrategyResult.Value { get { return this.Value; } }
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class RegisterActionAttribute : Attribute
{
public List<string> Dependencies { get; private set; }
public RegisterActionAttribute(params string[] depdencies)
{
this.Dependencies = new List<string>(depdencies);
}
}
public class StrategyAction
{
public string Name;
public List<string> Dependencies;
}
public abstract class BasePlugin<T> : IStrategy
{
public IStrategyResult Execute(ISerializable info = null)
{
return new StrategyResult<T>(this.execute(info));
}
protected abstract T execute(ISerializable info);
}
Example plugin:
[RegisterAction]
public class MyFirstPlugin: BasePlugin<bool>
{
protected override bool execute(ISerializable info = null)
{
try
{
// do stuff
return true;
}
catch (Exception)
{
return false;
}
}
}
[RegisterAction("MyFirstPlugin")]
public class MySecondPlugin: BasePlugin<string>
{
protected override string execute(ISerializable info = null)
{
try
{
// do stuff
return "success";
}
catch (Exception)
{
return "failed";
}
}
}
Example execution engine:
public class Engine
{
public List<StrategyAction> registeredActions = new List<StrategyAction>();
private List<StrategyAction> queuedActions = new List<StrategyAction>();
public IStrategyResult Execute(string action, ISerializable info = null)
{
if (this.registeredActions.FirstOrDefault(a=>a.Name == action) == null) return null;
// This code did not appear to be used anyway
//var returnType = typeof (IStrategyResult<>); //var genericReturn = returnType.MakeGenericType(strategyAction.ResponseType);
var instance = (IStrategy) UnityManager.Container.Resolve(typeof(IStrategy), action);
return instance.Execute(info);
}
public List<IStrategyResult> ExecuteQueuedActions()
{
var results = new List<IStrategyResult>();
var actions = this.queuedActions.AsQueryable();
var sortedActions = TopologicalSort.Sort(actions, action => action.Dependencies, action => action.Name);
foreach(var strategyAction in sortedActions)
{
this.queuedActions.Remove(strategyAction);
results.Add(Execute(strategyAction.Name));
}
return results;
}
}
Note that when the plugins are loaded, the RegisterActionAttribute information along with the name of the plugin type loaded need to be combined into a StrategyAction instance and loaded into the registeredActions field of the engine.
The above allows the plugins to work with strong types but still allows the engine to deal with a variety of types. If you need the engine to work with more strongly typed data, then please provide an example of how the callers of ExecuteQueuedActions are expected to work with the results from ExecuteQueuedActions.
You got into this pickle by giving your RegisterActionAttribute constructor the returnType argument. Since you have only one Execute() method, you are forced to deal with the fact that the return type can be different types.
Using dynamic is about as good as it gets. You can make Execute() generic but then you'll have to deal with a mismatch between its type parameter and the attribute's ResponseType. Not one that the compiler can catch, this fails at runtime. It isn't generic.
Frankly, this strongly sounds like one flexibility too many. At the risk of interpreting the point of having a return type incorrectly, the outcome of a "registration action" is rather boolean. It worked or it didn't work. And is in fact the way you implemented it, your first plugin snippet does return bool.
With very high odds that you should not use bool either. Failure ought to make a bang, you'd throw an exception.
Why not define a super interface IStrategyResult like this:
interface IStrategyResult
{
Type ReturnType { get; }
}
interface IStrategyResult<T> : IStrategyResult
{
// your code here
}
Then define your execute like this:
public IStrategyResult Execute(string action, params object[] parameters)
And have your StrategyResult : IStrategyResult<T> class set the property to return typeof(T)
By convention you could assume (or enforce using inheritance on an abstract StrategyResult<T> : IStrategyResult<T> class) the T to be the same as the ReturnType property of the non-generic IStrategyResult interface.
I am trying to work out some generic interfaces that includes a Dictionary and the items that it contains, both of which currently look like the code below.
As you can see I punted on the Dictionary, making the value be object. I would ideally like an interface with a covariant KEy of TParentMOdel and a covariant value of TModel, just like the item is, but I haven't been able to work that out so far (not sure its possible either).
What I do have seems to work until I try to add the last item in the last usage example below. The GenderVm is essentially an ISatteliteVm
It seems like the problem is with Gender being an Enum, which doesn't fully make sense to me. TParentModel in this case is Person, which is a subclass of Party. Covariance seems to be working here as I can add other items where TParentModel is a Person.
Which is why I say it seems like the problem is the value Gender. It's an Enum, and although an Enum is an object I think the type constraint system doesn't support Enums.
Is there an easy fix, such as a cast? A Does anyone see a better way to design SatelliteMap?
Cheers,
Berryl
Item
public interface ISatelliteVm<out TParentModel, out TModel> : ISatelliteVm
{
TParentModel ParentModel { get; }
TModel Model { get; }
}
Dictionary
public class SatelliteVmMap<TParentModel> : Dictionary<Type, ISatelliteVm<TParentModel, object>>, IEditableObject, IIsDirty
{
public void Add(ISatelliteVm<TParentModel, object> item) {
if (item == null)
throw new ArgumentNullException("item");
Add(item.GetType(), item);
}
}
Usage (abstract class that contains the SatelliteMap)
public interface IHubViewModel<out TModel> where TModel : Entity
{
public void AddSatelliteVm(ISatelliteVm<TModel, object> vm) {
if (_satelliteVmMap == null) {
_satelliteVmMap = new SatelliteVmMap<TModel>();
}
if (_satelliteVmMap.ContainsKey(vm)) return;
_satelliteVmMap.Add(vm);
}
}
Usage (subclass that contains several entries of ISatelliteVm)
public abstract class PartyDetailVm : HubViewModel<Party>
{
...
public LifespanVm LifespanVm { get { return GetSatelliteVm<LifespanVm>(); } }
public AvatarVm AvatarVm { get { return GetSatelliteVm<AvatarVm>(); } }
public TelecomNumberPcmShellVm TelecomNumberPcmShellVm { get { return GetSatelliteVm<TelecomNumberPcmShellVm>(); } }
...
}
Usage (subclass that contains several entries of ISatelliteVm)
public class PersonDetailVm : PartyDetailVm
{
...
public PersonNameVm PersonNameVm { get { return GetSatelliteVm<PersonNameVm>(); } }
public HonorificVm HonorificVm { get { return GetSatelliteVm<HonorificVm>(); } }
// THIS is the problem child I cannot add to the map
** public GenderVm GenderVm { get { return GetSatelliteVm<GenderVm>(); } } **
}
ERROR
Error 82 Argument 1: cannot convert from 'Parties.Presentation.ViewModels.PimDetailVms.PersonDetailVms.GenderVm' to
'Core.Presentation.Wpf.ViewModels.MasterDetailVms.DetailVms.SatelliteVms.ISatelliteVm'
Edit for Billy
Billy, SatelliteVm is just a base class that implements ISatelliteVm. Person is a subclass of Party and Gender is an enum.
public class GenderVm : SatelliteViewModel<Person, Gender>
{
}
Changes to GenderVm that seem to solve the problem (not sure why!)
public class GenderVm : SatelliteViewModel<Person, Gender>, ISatelliteVm<Party, object>
{
Party ISatelliteVm<Party, object>.ParentModel { get { return base.ParentModel; } }
object ISatelliteVm<Party, object>.Model { get { return base.Model; } }
}
See the documentation:
Variance in generic interfaces is supported for reference types only. Value types do not support variance. For example, IEnumerable<int> cannot be implicitly converted to IEnumerable<object>, because integers are represented by a value type.
This must address your issue.
Maybe you should change Gender to be a class?
I have a concrete class that contains a collection of another concrete class. I would like to expose both classes via interfaces, but I am having trouble figuring out how I can expose the Collection<ConcreteType> member as a Collection<Interface> member.
I am currently using .NET 2.0
The code below results in a compiler error:
Cannot implicitly convert type
'System.Collections.ObjectModel.Collection<Nail>' to
'System.Collections.ObjectModel.Collection<INail>'
The commented attempt to cast give this compiler error:
Cannot convert type
'System.Collections.ObjectModel.Collection<Nail>' to
'System.Collections.ObjectModel.Collection<INail>' via a
reference conversion, boxing conversion, unboxing conversion, wrapping
conversion, or null type conversion.
Is there any way to expose the collection of concrete types as a collection of interfaces or do I need to create a new collection in the getter method of the interface?
using System.Collections.ObjectModel;
public interface IBucket
{
Collection<INail> Nails
{
get;
}
}
public interface INail
{
}
internal sealed class Nail : INail
{
}
internal sealed class Bucket : IBucket
{
private Collection<Nail> nails;
Collection<INail> IBucket.Nails
{
get
{
//return (nails as Collection<INail>);
return nails;
}
}
public Bucket()
{
this.nails = new Collection<Nail>();
}
}
C# 3.0 generics are invariant. You can't do that without creating a new object. C# 4.0 introduces safe covariance/contravariance which won't change anything about read/write collections (your case) anyway.
Just define nails as
Collection<INail>
Why not just return it as an interface, just have all your public methods in the interface, that way you don't have this problem, and, if you later decide to return another type of Nail class then it would work fine.
What version of .Net are you using?
If you are using .net 3.0+, you can only achieve this by using System.Linq.
Check out this question, which solved it for me.
There is one solution that might not be quite what you are asking for but could be an acceptable alternative -- use arrays instead.
internal sealed class Bucket : IBucket
{
private Nail[] nails;
INail[] IBucket.Nails
{
get { return this.nails; }
}
public Bucket()
{
this.nails = new Nail[100];
}
}
(If you end up doing something like this, keep in mind this Framework Design Guidelines note: generally arrays shouldn't be exposed as properties, since they are typically copied before being returned to the caller and copying is an expensive operation to do inside an innocent property get.)
use this as the body of your property getter:
List<INail> tempNails = new List<INail>();
foreach (Nail nail in nails)
{
tempNails.Add(nail);
}
ReadOnlyCollection<INail> readOnlyTempNails = new ReadOnlyCollection<INail>(tempNails);
return readOnlyTempNails;
That is a tad bit of a hacky solution but it does what you want.
Edited to return a ReadOnlyCollection. Make sure to update your types in IBucket and Bucket.
You can add some generics. Fits better, more strongly coupled.
public interface IBucket<T> where T : INail
{
Collection<T> Nails
{
get;
}
}
public interface INail
{
}
internal sealed class Nail : INail
{
}
internal sealed class Bucket : IBucket<Nail>
{
private Collection<Nail> nails;
Collection<Nail> IBucket<Nail>.Nails
{
get
{
return nails; //works
}
}
public Bucket()
{
this.nails = new Collection<Nail>();
}
}
This way the Collection<Nail> you return from Bucket class can only ever hold Nails. Any other INail wont go into it. This may or may not be better depending on what you want.
Only if you want Collection<INail> (the interface property) you return from Bucket to hold other INails (than Nails) then you may try the below approach. But there is a problem. On one side you say you want to use a private Collection<Nail> in Bucket class and not a Collection<INail> because you dont want to accidentally add other INails from Bucket class into it but on the other side you will have to add other INails from outside of Bucket class. This is not possible on the same instance. Compiler stops you from accidentally adding any INail to a Collection<Nail>. One way is to return a different instance of Collection<INail> from your Bucket class from the existing Collection<Nail>. This is less efficient, but could be the semantics you are after. Note that this is conceptually different from above
internal sealed class Bucket : IBucket
{
private Collection<Nail> nails;
Collection<INail> IBucket<Nail>.Nails
{
get
{
List<INail> temp = new List<INail>();
foreach (Nail nail in nails)
temp.Add(nail);
return new Collection<INail>(temp);
}
}
public Bucket()
{
this.nails = new Collection<Nail>();
}
}
C# doesn't support generic collections covariance (it's only supported for arrays).
I use an adapter class in such cases. It just redirects all calls to the actual collection, converting values to the required type (doesn't require copying all list values to the new collection).
Usage looks like this:
Collection<INail> IBucket.Nails
{
get
{
return new ListAdapter<Nail, INail>(nails);
}
}
// my implementation (it's incomplete)
public class ListAdapter<T_Src, T_Dst> : IList<T_Dst>
{
public ListAdapter(IList<T_Src> val)
{
_vals = val;
}
IList<T_Src> _vals;
protected static T_Src ConvertToSrc(T_Dst val)
{
return (T_Src)((object)val);
}
protected static T_Dst ConvertToDst(T_Src val)
{
return (T_Dst)((object)val);
}
public void Add(T_Dst item)
{
T_Src val = ConvertToSrc(item);
_vals.Add(val);
}
public void Clear()
{
_vals.Clear();
}
public bool Contains(T_Dst item)
{
return _vals.Contains(ConvertToSrc(item));
}
public void CopyTo(T_Dst[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public int Count
{
get { return _vals.Count; }
}
public bool IsReadOnly
{
get { return _vals.IsReadOnly; }
}
public bool Remove(T_Dst item)
{
return _vals.Remove(ConvertToSrc(item));
}
public IEnumerator<T_Dst> GetEnumerator()
{
foreach (T_Src cur in _vals)
yield return ConvertToDst(cur);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public override string ToString()
{
return string.Format("Count = {0}", _vals.Count);
}
public int IndexOf(T_Dst item)
{
return _vals.IndexOf(ConvertToSrc(item));
}
public void Insert(int index, T_Dst item)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
public T_Dst this[int index]
{
get { return ConvertToDst(_vals[index]); }
set { _vals[index] = ConvertToSrc(value); }
}
}
you could use the Cast extension
nails.Cast<INail>()
I can't test it here to provide a more comprehensive example, as we are using .NET 2.0 at work (gripe gripe), but I did have a similar question here