Sorry if question name wasn't that explicit, but I couln't find a better one...
Here's my problem :
I have a class :
public class WatchableVariable<T>{
...
}
A Container Class :
public class CommonWatchableVariableContainer
{
private List<WatchableVariable<IComparable>> Watchables;
...
public void Add<T>(string name) where T : IComparable
{
WatchableVariable<T> test = new WatchableVariable<T>(name);
Watchables.Add(test);
}
...
}
And have a strange error :
Which, IMO, should work since i'm applying a "IComparable" constraint on my generic type.
could someone point me out where I might have messed up ?
EDIT : I already tried to implement IComparable on Class WatchableVariable which resulted in the exact same error :/
public class WatchableVariable<T> : ObservableObject where T : IComparable
{ ... }
EDIT 2 : While trying to implement the solution proposed by canton7, I realized I may be blocked.
Here's the interface I created :
public interface IWatchableVariable<out T>
{
T Variable { get; set; }
}
Here's what I'm trying to achieve :
double dVar1 = process();
double dVar2 = process();
double dVar3 = process();
bool bVar1 = process();
CommonWatchableVariableContainer container = new CommonWatchableVariableContainer ();
container.Add<bool>("myTrackedVariable_1");
container.Add<double>("myTrackedVariable_2");
container["myTrackedVariable_1"].Variable = dVar1; //Variable would be a property of type T (which would be double in this case)
container["myTrackedVariable_2"].Variable = bVar1;
foreach(IWatchableVariable WV in container){
process(WV);
}
container.Remove("myTrackedVariable_1");
Does this make things clearer ?
Your collection is a List<WatchableVariable<IComparable>>
. You're trying to add a WatchableVariable<T>
to it.
This is not allowed, because of the rules of generic variance. Let's pretend that your WatchableVariable<T>
class defined this method:
public class WatchableVariable<T>
{
public void Set(T value);
}
If you've got a WatchableVariable<Foo>
, you can call watchableVariable.Set(new Foo())
, but not watchableVariable.Set((IComparable)foo)
. That makes sense.
Take that to your situation, and someone could call Add<Foo>("")
, so Watchables
contains a WatchableVariable<Foo>
. They then access Watchables[0]
, which gives them a WatchableVariable<IComparable>
, and call Set((IComparable)bar)
. This would be unsafe, as we previously said that a WatchableVariable<Foo>
's Set(T value)
method will only accept a Foo
, but we've just given it any old IComparable
!
The way around this is using covariance, which is only available on interfaces.
First, we declare a generic interface, and we promise that you can only ever take T
's out of it, and never put them in, using out T
:
public interface IWatchableVariable<out T>
{
// The compiler stops you from defining any methods which accept a T
}
public class WatchableVariable<T> : IWatchableVariable<T>
{
// ...
}
Then you can write:
private List<IWatchableVariable<IComparable>> Watchables;
And everything will work.
(A similar thing is available using contravariance and in T
, but that isn't applicable to your situation).
In response to your Edit 2
Your edit contains the comment
// Variable would be a property of type T (which would be double in this case)
That's a run-time assertion. You will have to define your interface like this:
public interface IWatchableVariable<out T>
{
object Variable { get; set; }
}
Since Variable
isn't of type T
, this is allowed. Then your WatchableVariable<T>
class will have to do a run-time cast, to make sure that Variable
was set to a compatible type. You can use an explicit interface implementation to make this a bit neater.
public class WatchableVariable<T>
{
public T Variable { get; set; }
object IWatchableVariable<T>.Variable
{
get => Variable;
set => Variable = (T)value;
}
}
You can make this slightly neater by doing something like:
public interface IWatchableVariable<out T>
{
T Variable { get; }
void SetVariable(object value);
}
public class WatchableVariable<T> : IWatchableVariable<T>
{
public T Variable { get; set; }
public void SetVariable(object value)
{
Variable = (T)value;
}
}
I don't know what are you trying to achieve, but to make that work you should: declare your type with an IComparable
constraint.
public class WatchableVariable<T> where T : IComparable
{
public WatchableVariable(string name) { }
}
Then make your second class generic with the same constraint.
public class CommonWatchableVariableContainer<T> where T : IComparable
{
private List<WatchableVariable<T>> Watchables;
public void Add(string name)
{
var test = new WatchableVariable<T>(name);
Watchables.Add(test);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.