C# Is Not Assignable Type - Generics - c#

So I'm just hacking around with a state machine type I was working on and mostly wanting to just try out the Activator.CreateInstance method to see what it was like, and I ran into a problem where I cant seem to use the where clause as I would think. I apologize ahead of time if I am just an idiot and everyone laughs me out of here. So I have 2 small classes.
public class TransitionContainer<TTransition, TStateTo> :
ITransitionContainer<TTransition, TStateTo>
where TTransition : ITransition
where TStateTo : IState
{
public TransitionContainer()
{
StateTo = typeof(TStateTo);
Transition = Activator.CreateInstance<TTransition>();
}
public Type StateTo { get; private set; }
public TTransition Transition { get; private set; }
}
as well as
public class StateContainer<T> : IStateContainer<T> where T : IState
{
private Dictionary<Type, TransitionContainer<ITransition, IState>> _transitions =
new Dictionary<Type, TransitionContainer<ITransition, IState>>();
public StateContainer()
{
State = Activator.CreateInstance<T>();
}
public T State { get; private set; }
public int TransitionCount
{
get { return _transitions.Count; }
}
public void AddTransition<TTransition, TStateTo>() where TTransition : ITransition, new()
where TStateTo : IState, new()
{
var transitionContainer= new TransitionContainer<TTransition, TStateTo>();
_transitions.Add(typeof(TTransition), transitionContainer);
}
So on the line _transitions.Add(typeof(TTransition), transitionContainer); I receive a cannot convert TransitionContainer<TTransition,TStateTo> expression to type TransitionContainer<ITransition,IState> error.
If I change the generic parameters to
var transitionContainer= new TransitionContainer<ITransition, IState>();
it works fine, but I wanted to use inherited types that are new() so I could be sure I could instantiate them.
Again I apologize if I'm doing something incredibly wrong, I was just kind of ran into a brick wall and my googling led me in no good direction. I didnt include any of the other interfaces or classes as they didn't seem to be part of the problem, but if there needed I can attach them. Thanks for any help!

This issue happens because:
ITransitionContainer is not a covariant interface over its type arguments.
AddTransition method generic arguments are not constrained to be reference types.
_transitions is not a dictionary with ITransitionContainer values, so without changing it to Dictionary<Type, ITransitionContainer<ITransition, IState>> we still won't be able to add even properly resticted covariant transtions.
Simplified example
Consider the following simplified case:
public interface ITransition
{
}
public class SomeTransition : ITransition
{
}
public interface ITest<TTransition>
where TTransition : ITransition
{
TTransition Value { get; }
}
public class SomeTest<TTransition> : ITest<TTransition>
where TTransition : ITransition
{
public TTransition Value
{
get
{
throw new NotImplementedException();
}
}
}
It will fail in both
public static void Do<TTransition>()
where TTransition : ITransition
{
ITest<ITransition> item = new SomeTest<TTransition>();
}
and
ITest<ITransition> item = new SomeTest<SomeTransition>();
If you make ITest covariant
public interface ITest<out TTransition>
, then it will fail only in generic method. Because here TTransition can be a struct and co/(contra)variance doesn't work with value types:
public static void Do<TTransition>()
where TTransition : ITransition
{
ITest<ITransition> item = new SomeTest<TTransition>();
}
But if you make that method constrained to only reference types, then it will work in both cases:
public static void Do<TTransition>()
where TTransition : class, ITransition
{
ITest<ITransition> item = new SomeTest<TTransition>();
}
Apply the same principle(out and class) to your two generic arguments and it will do the job.
Full solution for your specific case:
public interface IState
{ }
public interface ITransition
{ }
// !!!!! - Here we add out specifier
public interface ITransitionContainer<out TTransition, out TStateTo>
where TTransition : ITransition
where TStateTo : IState
{
Type StateTo
{
get;
}
TTransition Transition
{
get;
}
}
public interface IStateContainer<T> where T : IState
{
T State
{
get;
}
}
public class TransitionContainer<TTransition, TStateTo> : ITransitionContainer<TTransition, TStateTo>
where TTransition : ITransition
where TStateTo : IState
{
public TransitionContainer()
{
StateTo = typeof(TStateTo);
Transition = Activator.CreateInstance<TTransition>();
}
public Type StateTo { get; private set; }
public TTransition Transition { get; private set; }
}
public class StateContainer<T> : IStateContainer<T> where T : IState
{
private Dictionary<Type, ITransitionContainer<ITransition, IState>> _transitions =
new Dictionary<Type, ITransitionContainer<ITransition, IState>>();
public StateContainer()
{
State = Activator.CreateInstance<T>();
}
public T State { get; private set; }
public int TransitionCount
{
get { return _transitions.Count; }
}
public void AddTransition<TTransition, TStateTo>()
// !!!!!! - Here we add class constraints
where TTransition : class, ITransition, new()
where TStateTo : class, IState, new()
{
var transitionContainer = new TransitionContainer<TTransition, TStateTo>();
_transitions.Add(typeof(TTransition), transitionContainer);
}
}

That fails because generics are not covariant. The problem can be seen here:
TransitionContainer<ITransition, IState> value = new TransitionContainer<TTransition, TStateTo>();
That gives you the same error. You also get this error with something as simple as:
List<IComparable> list = new List<DateTime>();
Visual Studio tells you (basically) that:
Cannot implicitly convert type 'List<System.DateTime>' to 'List<System.IComparable>'
What you need to do is convert the object. You could create a Convert method that returns a TransitionContainer<ITransition, IState> and then use .Add(typeof(TTransition), transitionContainer.Convert()) (or whatever you name it).
But the most painless option is to create an implicit conversion for your TransitionContainer<TTransition, TStateTo> object by adding this static method:
public static implicit operator TransitionContainer<ITransition, IState>(TransitionContainer<TTransition, TStateTo> value)
{
return new TransitionContainer<ITransition, IState>() { StateTo = value.StateTo, Transition = value.Transition };
}
And that's it. :)
Of course, you will have to copy everything needed for it to work, in this case it seems these two objects are enough.

Related

Cannot implicitly convert A<B> to A<C<D>> where B inherit from C<D>

I'm building a sort of library to perform text replacement in a document based on some rule. We built a POC and now I'm trying to create a library as generic as possible.
I have just one problem with inheritance:
This is the simplified representation of the classes/interfaces I'm dealing with:
public interface IRule {}
public interface IReplaceRule<T> : IRule
{
T ReplaceValue { get; set; }
}
public class CachedRules<T> where T : IReplaceRule<object>
{
#region Props
public T RuleTemplate { get; set; }
public IDictionary<string, T> RuleList { get; private set; } = null;
#endregion
public void SetRuleList(IDictionary<string, T> ruleList) { ... }
public bool ContainsRuleByKey(string key) { ... }
public bool TryGetRuleValueByKey(string key, out T rule) { ... }
}
public class SingleRowRule : IReplaceRule<string> { ... }
I also have a class which is like a repository of rules, and inside it I can add as many CachedRules as I need:
public class RulesStorage : AbstractRulesStorage
{
private CachedRules<SingleRowRule> singleRowRules;
public RulesStorage() { ... }
// Bunch of methods not useful for this question
// Here I need to return a list of ChachedRule, but just ofr testing I tried to return only one
public CachedRules<IReplaceRule<object>> GetCachedReplaceRules()
{
return singleRowRules;
}
}
Inside this class I need a method to return all the CachedRules declared in the RulesStorage:
Unfortunately the RulesStorage.GetCachedReplaceRules method give me this error:
Cannot implicitly convert type TestLib.Model.CachedRules<TestLib.Rules.SingleRowRule> to TestLib.Model.CachedRules<TestLib.Abstractions.IReplaceRule<object>
I really don't like the fact that I had to put <object> since IReplaceRule requires a generic and also I'm stuck because I don't know how to return this list of CachedRules without getting this compilation error.
Do you have some idea? Do I have to organize the code differently in your opinion?
Hope I've made myself clear and thanks in advance!
Instead of doing IReplaceRule<object> you can do it the way IEnumerable<T> inherits from IEnumerable. With that minor tweak in place, I create an implicit converter to go from T to IReplaceRule and the constraint in place now ensures I can actually do this safely.
I'm assuming you have a reason to have private CachedRules<SingleRowRule> singleRowRules; and can't just using private CachedRules<IReplaceRule> singleRowRules; which would remove the need for this extra conversion hop.
Code:
public interface IReplaceRule : IRule { object ReplaceValue { get; set; } }
public interface IReplaceRule<T> : IReplaceRule { new T ReplaceValue { get; set; } }
public class CachedRules<T> where T : IReplaceRule
{
public IDictionary<string, T> RuleList { get; private set; } = new Dictionary<string, T>();
//The key ingredient for a nice experience instead of just doing this in the method
public static implicit operator CachedRules<IReplaceRule>(CachedRules<T> rules)
=> new CachedRules<IReplaceRule> { RuleList = rules.RuleList.ToDictionary(x => x.Key, x => x.Value as IReplaceRule) };
}
public class SingleRowRule : IReplaceRule<string>
{
public string ReplaceValue { get; set; }
object IReplaceRule.ReplaceValue { get => ReplaceValue; set => ReplaceValue = value as string; }
}
public class RulesStorage
{
private CachedRules<SingleRowRule> singleRowRules = new CachedRules<UserQuery.SingleRowRule>();
//FIXME: just for testing purposes
public RulesStorage() => singleRowRules.RuleList.Add("Hello", new SingleRowRule { ReplaceValue = "World" });
// Here I need to return a list of ChachedRule, but just ofr testing I tried to return only one
public CachedRules<IReplaceRule> GetCachedReplaceRules() => singleRowRules;
}

C# - creating a class whose constructor takes generic param results in casting exception

I have IInfo and its generic version:
public interface IInfo
{
IInput Input { get; }
}
public interface IInfo<T> : IInfo where T : IInput
{
new T Input { get; }
}
Class implementation:
public class Info : IInfo<IInput>
{
public IInput Input { get; }
public Info (IInput input) {}
}
Factory to create IOutput from IInput:
public class GenericFactory<TInput, TOutput> where TInput : IInput where TOutput : IOutput
{
public IOutput Create(IInfo info)
{
ConstructorInfo cInfo = typeof(TOutput).GetConstructor(new[] { typeof(IInfo<TInput>) });
object output = cInfo.Invoke(new object[] {cInfo});
}
}
To test the above code:
public class TestInput : IInput
{
}
public abstract class AbstractOutput<TInput> : IOutput where TInput : IInput
{
}
public class TestOutput: AbstractOutput<TestInput>
{
public TestOutput(IInfo<TestInput> info)
{
}
}
public void Test()
{
IInput input = new TestInput();
IInfo info = new Info(input);
var factory = new GenericFactory<TestInput, TestOutput>();
IOutput output = factory.Create(info);
}
I get the following error:
Object of type 'Info' cannot be converted to type'Info<TestInput>'.
Side note: I'm open to any suggestions to simplify/re-write the code in a different way.
public TestOutput(IInfo<TestInput> info)
{
}
Is expecting an IInfo<TestInput> explicitly. However, you're trying to call it with IInfo<IInput> (which is what Info is designed to be).
To make it clear, you could also write:
IInput input = new OtherInput();
IInfo info = new Info(input);
var factory = new GenericFactory<TestInput, TestOutput>();
IOutput output = factory.Create(info);
And now you've provided IInfo<OtherInput> to something expecting IInfo<TestInput>
You would need to make IInfo<T> contravariant to allow it to be cast, for example:
public interface IInfo<in T> : IInfo
where T : IInput
{
//new T Input { get; }
}
But note that it's illegal to return T when with a contravariant interface. The alternative is to make Info generic, and change Create to accept IInfo<TInput>. That latter gives you the benefit of a compile-time error when trying to pass IInfo<OtherInput> to Create(), rather than a run-time error

Generic container: why do I need an interface?

Say I have the following
public interface IInterval<T>
{
T Start { get; }
T Stop { get; }
}
public class DateTimeInterval : IInterval<DateTime>
{
private DateTime _start;
private DateTime _stop;
public DateTimeInterval(DateTime start, DateTime stop)
{
_start = start; _stop = stop;
}
public DateTime Start
{
get { return _start; }
}
public DateTime Stop
{
get { return _stop; }
}
}
public class SortedIntervalList<T>
where T : IInterval<T>, IComparable<T>
{
}
If I were to now try to instantiate the container
var test = new SortedIntervalList<DateTimeInterval>();
I get a compilation error
The type 'Test' cannot be used as type parameter 'T' in the generic
type or method TestContainer<T>. There is no implicit reference
conversion from 'Test' to ITest<Test>.
Why is this?
Note on edit history
For clarity, classes for the original question are included below
public interface ITest<T>
{
int TestMethod();
}
public class Test : ITest<bool>
{
public int TestMethod()
{
throw new NotImplementedException();
}
}
public class TestContainer<T>
where T : ITest<T>
{
}
where T : ITest<T>
Your class inherits ITest<bool>, which is not ITest<T> for your T (Test).
As the error is trying to tell you, that does not meet your generic constraint, so you can't do that.
Because you expect your T in TestContainer<T> to be ITest<T>. that doesn't make sense. I think you meant :
public class TestContainer<C, T>
where C : ITest<T>
{
}
For your updated code in your question:
public class SortedIntervalList<C, T>
where C : IInterval<T>, IComparable<T>
{ }
With:
test = new SortedIntervalList<DateTimeInterval, DateTime>();

How to create a custom control factory method

I have a skinnable Control library that was loads control settings/properties from external xml files.
The Xml classes are in a seperate project as these will be used in a skin editor application, now the question, The controls accept an xml object in the constructor to build the Control but I need to find a nice way to create each control.
Xml class example:
[Serializable]
[XmlInclude(typeof(XmlButton))]
[XmlInclude(typeof(XmlGroup))]
[XmlType(TypeName="Control")]
public class XmlControl
{
[DefaultValue(0)]
public int Width { get; set; }
[DefaultValue(0)]
public int Height { get; set; }
...
and derived types per control type
[Serializable]
[XmlType(TypeName = "Button")]
public class XmlButton : XmlControl
{
public string Label { get; set; }
}
Control classes
public class GUIControl : GUI3DBase
{
public GUIControl(XmlControl skinXml)
{
SkinXml = skinXml;
...
public class GUIButton : GUIControl, IActionControl
{
public GUIButton(XmlControl skinXml) : base(skinXml)
{
}
...
Now this is where I need help, at the moment I have a Method to create controls based on the xml object passed in.
public static GUIControl CreateControl<T>(T skinXml) where T : XmlControl
{
if (skinXml is XmlButton)
{
return new GUIButton(skinXml);
}
else if (skinXml is XmlGroup)
{
return new GUIGroup(skinXml);
}
....
I have about 30 controls and the "if ladder" is growing fast and I feel like I am missing a simple way to create thes controls withou needing to check the xml object type then create the corresponding control type.
I can't add a Type property in the Xml object as that would create circular dependency.
Any help on a good factory method or new stucture layout would be awesome
Maybe IDictionary<Type, Func<XmlControl, GUIControl>> would help. Something like this:
private static Dictionary<Type, Func<XmlControl, GUIControl>> _dictionary = new Dictionary<Type, Func<XmlControl, GUIControl>>()
{
{typeof (XmlControlImpl), x => new GUIControl(x)},
{typeof (XmlGroup), x => new GUIGroup(x)},
};
public static GUIControl CreateControl<T>(T skinXml) where T : XmlControl
{
Func<XmlControl, GUIControl> builder;
if (!_dictionary.TryGetValue(typeof(T), out builder))
throw new KeyNotFoundException("");
return builder(skinXml);
}
Ok, I have found a way to do this with all your ideas and a little reflection, not sure if its the best way but it works nicly and adding a new skinnable control only requires a new xml object and an attribute on the control class.
Attribute Class
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class XmlControlTypeAttribute : Attribute
{
protected Type xmlType;
public XmlControlTypeAttribute(Type xmlType)
{
this.xmlType = xmlType;
}
public Type XmlType
{
get { return this.xmlType; }
}
}
Control:
[XmlControlType(typeof(XmlButton))]
public class GUIButton : GUIControl, IActionControl
{
public GUIButton(XmlControl skinXml) : base(skinXml)
{
}
....
}
Factory method:
public static GUIControl CreateControl2<T>(T skinXml) where T : XmlControl
{
var controlType = Assembly.GetExecutingAssembly().DefinedTypes
.Where(t => t.BaseType == typeof(GUIControl) && t.GetCustomAttribute<XmlControlTypeAttribute>().XmlType.Equals(typeof(T)))
.FirstOrDefault();
return (GUIControl)Activator.CreateInstance(controlType, new[] { skinXml }, null);
}
Thanks for all the ideas the helped heaps, I will leave this question open a bit longer incase somome has a better solution than this.
I would be tempted to add an abstract method to XmlControl:
public abstract class XmlControl
{
[DefaultValue(0)]
public int Width { get; set; }
[DefaultValue(0)]
public int Height { get; set; }
public abstract Type ControlType();
override it in each implementation eg:
public class XmlButton : XmlControl
{
public string Label { get; set; }
public override Type ControlType(){ return typeof(GUIButton); }
}
And then use reflection in the Factory method to construct the right class:
public static GUIControl CreateControl<T>(T skinXml) where T : XmlControl
{
return (GUIControl)Activator.CreateInstance(skinXml.ControlType(),
new[]{skinXml},null);
}

Generic interface compiles but does not work at runtime

I have an issue with generic interface. The compiler does not give any compiling errors but at run-time unseen exception is thrown.
public interface IStructure
{
string Name {get;}
}
public interface IStructureNavigation<T> : IStructure where T : IStructure
{
T Parrent {get;}
}
public class ResourceStructure : IStructureNavigation<ResourceStructure>
{
private ResourceStructure _parrent;
public virtual string Name
{
get;
set;
}
public virtual ResourceStructure Parrent
{
get { return _parrent; }
}
}
Can someone explain why does the following code fail at runtime?
public class Action
{
private ObjectContext _context;
private ObjectSet<ResourceStructure> _structue;
private IQueryable<ResourceStructure > _parrents;
public Action()
{
string connectionString =
ConfigurationManager
.ConnectionStrings["Structure"].ConnectionString;
_context = new ObjectContext(connectionString);
_context.ContextOptions.LazyLoadingEnabled = true;
_structue = _context.CreateObjectSet<ResourceStructure>();
_parrents = _structue.Where(x => x.ParentID == null);
// FAILS IN FOREACH LOOP : UNSEEN EXCPTION
foreach (IStructureNavigation<IStructure> strt in _parrents)
{
//do something
}
//WORKS IF USING CONCRETE TYPE NOT INTERFACE
foreach(IStructureNavigation<ResourceStructure > strt in _parrents)
{
//do something
}
}
}
Declare T as covariant
public interface IStructureNavigation<out T> : IStructure where T : IStructure
That's because your instance is of type IStructureNavigator<ResourceStructure> and not IStructureNavigator<IStructure>.
If you need to use the interface, you can use the Cast extension method:
_parrents = _context.CreateObjectSet<ResourceStructure>().Cast<IStructure>();
Which version of the Framework are you using?

Categories