Why doesn't List.Contains work with my code? - c#

I'm using List.Contains to tell whether a variable is inside the list or not, but it keeps on returning that it isn't when it is.
I've looked up MSDN and I've noticed that I have to inherit from IEquatable and implement my own .Equals method. The actual class is inheriting from another one, so I've written the .Equals method in the base class.
Here's the code of the class "Actividad":
abstract public class Actividad:IEquatable<Actividad> {
protected int codigo;
[...]
public bool Equals(Actividad otra)
{
return this.Codigo == otra.Codigo;
}
}
and here's the definition of the child class "Actividad_a":
public class Actividad_a : Actividad{ [...] }
This is the code that checks whether something is inside the list:
private void loadDisponibles() {
foreach (Actividad_a act in Program.Asignaturas) {
if (!user1.ActAcademicas.Contains(act)) {
doSomething();
}
}
}
Program.Asignaturas and user1.ActAcademicas are both defined as List<Actividad_a>.
The problem is that !user1.ActAcademicas.Contains(act) always returns true, no matter the data is in the list or not.
My first guess is that I have to inherit from IEquatable and implement .Equals method in each derived class, but I'm not really sure about it.

You're comparing Actividad_a and the Contains method is expecting it to implement IEquatable<Actividad_a> rather than IEquatable<Actividad>
Try overriding the default Equals method as well
public override bool Equals(object otra)
{
var actividad = otra as Actividad;
if (actividad == null) return false;
return this.Codigo == actividad.Codigo;
}
EDIT:
Some more info: .NET 4.0 introduced flexibility for working with generics called Contravariance and Covariance http://msdn.microsoft.com/en-us/library/dd799517.aspx
Covariant and contravariant generic type parameters provide greater
flexibility in assigning and using generic types. For example,
covariant type parameters enable you to make assignments that look
much like ordinary polymorphism. Suppose you have a base class and a
derived class, named Base and Derived. Polymorphism enables you to
assign an instance of Derived to a variable of type Base. Similarly,
because the type parameter of the IEnumerable(Of T) interface is
covariant, you can assign an instance of IEnumerable
(IEnumerable(Of Derived) in Visual Basic) to a variable of type
IEnumerable
In general, a covariant type parameter can be used as the return type
of a delegate, and contravariant type parameters can be used as
parameter types. For an interface, covariant type parameters can be
used as the return types of the interface's methods, and contravariant
type parameters can be used as the parameter types of the interface's
methods.
For some reason IEquatable<T> was not made contravariant and why your original approach won't work

Related

Is it possible to use the generic "in" and "out" modifier on the same T?

This code is invalid because T can't have the in and out modifier at the same time:
public interface IInOut<in out T>
{
}
But you can do this "workaround":
public interface IInOutWorkaround<in TIn, out TOut>
{
TOut Test(TIn value);
}
public class InOutWorkaround<T> : IInOutWorkaround<T, T>
{
public T Test(T value)
{
throw new NotImplementedException();
}
}
The second example works and the InOutWorkaround class has the same type for TIn and TOut, so why is it not possible to add both modifier to the same T directly in the interface? Or is it possible with a different syntax?
in T says that T can not be used covariantly, and out T says that T can not be used contravariantly. Your in out T would therefore mean that the type can not be used covariantly and can not be used contravariantly, which means it'd be invariant. So in effect that would behave identically to just writing public interface IInOut<T>, because when no in or out modifiers are used the generic type is considered invariant.
In the case of your class InOutWorkaround<T>, T is still invariant, so the fact that you're using it as both an in and out type is fine, because it's invariant, as it meets both restrictions. If you were attempting to have a type that could be used both covariantly and contravariantly, your workaround didn't achieve that, because the T in InOutWorkaround is invariant (because all generic type arguments for all classes are invariant). That generic type argument cannot be used either covariantly or contravariantly.
One could have interfaces IReadable<out T> { T read(int index); }, IWritable<in T> { void write(int index, T dat);, ISplitReadWrite<out Tout, in Tin>:IReadable<Tout>,IWritable<Tin>, and IReadWrite<T>:ISplitReadWrite<T,T>.
If one has a class MyCollection<T> which implements IReadWrite<T>, then a MyCollection<Cat> could be converted to IReadable<Animal>, IWritable<SiameseCat>, or an ISplitReadWrite<Animal,SiameseCat>. Note, however, that the only IReadable<T> that would yield an item that could be stored into a MyCollection<Cat> would be IReadable<Cat>, the only IWritable<T> that could handle everything that might appear in a MyCollection<Cat> would be IWritable<Cat>. The only forms of ISplitReadWrite<Tout,Tin> that would allow one to read out an item and write it back to the same collection without a cast would be those where the two types were the same, and the only such type implemented by MyCollection<Cat> would be ISplitReadWrite<Cat,Cat>.
Note that one could have an interface with methods that could be equally usable with MyCollection<Animal> and MyCollection<SiameseCat>, such as "swap the items in slots i1 and i2 of the same collection", but such an interface wouldn't need any generic parameter at all. Id one has an IPermutable interface, it could include methods like void swapItems(int i1, int i2); which wouldn't have any generic types in their signatures, and thus wouldn't make it necessary for the type to include any generic type arguments.
According to Extending Variant Generic Interfaces specification
The compiler does not infer the variance from the interface that is
being extended. You can create an interface that extends both the interface where the
generic type parameter T is covariant and the interface where it is
contravariant if in the extending interface the generic type parameter
T is invariant.
interface ICovariant<out T> { }
interface IContravariant<in T> { }
interface IInvariant<T> : ICovariant<T>, IContravariant<T> { }
This example looks like as an exactly your case, T is invariant generic type parameter in InOutWorkaround<T> interface, compiler doesn't infer (or inherit it in extending interface), so your workaround is pointless
public class InOutWorkaround<T> : IInOutWorkaround<T, T>
{
public T Test(T value)
{
throw new NotImplementedException();
}
}

Overloading methods of an Interface based on return type...

I cant seem to find anything that explicitly states this should never be done, not can i find a recommended method for doing it. so I am beginning to thing I am on an entirely wrong track here...
I am trying to overload a function based on return type in an interface. Basically I have 15 functions in an interface, 9 of them have the same name/params but different return type, I am trying to isolate this so I dont have to write 15 functions in the interface, I would just like a few..
public interface IController
{
IEnumerable<T> Fetch<T>();
}
from here I want to do implementations such as...
public IEnumerable<T> Fetch<T>() where T : *class*
{
return dbContext.*clas*.ToList();
}
however I recieve a compilation error of...
Error 1 The constraints for type parameter 'T' of method 'Controllers.Controller.Fetch<T>()' must match the constraints for type parameter 'T' of interface method 'IController.Fetch<T>()'. Consider using an explicit interface implementation instead.
anyone have any ideas on this one...
You can't do this because this implementation conflicts with the Liskov Substitution principle.
A method can only become more wide (accept more) than the the classes/interfaces above in the type hierarchy.
Now C# does not fully support the Liskov Substition principle (in the sense that widening parameters is not allowed). But it means for instance that if a method
public class Foo {
void Bar (T parameter);
}
is defined in the first level, that method cannot be override with
public class SubFoo : Foo {
void Bar (SubT parameter);
}
This is because one can call the Bar method of a SubFoo on the Foo level. And the Foo level has a contract that it accepts T. So making the types more narrow is not an option.
If one thus moves down in the class hierarchy one notices that:
return types become more narrow; and
parameters become wider
C# however supports variance/covariance on the interface level. If T is thus only used to specify the output type, one can indeed make T more narrow. This is called variance. You can specify this as:
public interface Foo<out T> {
T GetValue ();
}
It means that Foo<T> is a subclass of Foo<SuperT>. The same for covariance:
public interface Foo<in T> {
void SetValue (T value);
}
Making a few assumptions...if you're talking about an EF dbContext, you can actually do this:
public IEnumerable<T> Fetch<T>() where T : class
{
return dbContext.Set<T>().ToList();
}
More generally, you can do something like this, where the generic method delegates to various implementation methods for the different types:
public IEnumerable<T> Fetch<T>() where T : class
{
if (typeof(T) == typeof(X)) return FetchX();
//Handle other types here...
}
As Servy pointed out, to implement the above you would also need to modify your interface to include the class constraint (assuming you need it):
public interface IController
{
IEnumerable<T> Fetch<T>() where T: class;
}

Why does VS warn me that typeof(T) is never the provided type in a generic method where the type parameter is restricted to implement T?

I hope the question is correct, so let's give you an example. Imagine the following generic method:
public abstract class Base : IDisposable
{
public static IEnumerable<T> GetList<T>()
where T : Base
{
// To ensure T inherits from Base.
if (typeof(T) is Base)
throw new NotSupportedException();
// ...
}
}
According to the MSDN the keyword where restricts the type parameter T to be of type Base or to inherit from this class.
[...] a where clause can include a base class constraint, which states that a type must have the specified class as a base class (or be that class itself) in order to be used as a type argument for that generic type.
Also this code does compile:
public static T GetFirst()
where T : Base
{
// Call GetList explicitly using Base as type parameter.
return (T)GetList<Base>().First();
}
So when following the last code typeof(T) should return Base, shouldn't it? Why does Visual Studio then prints this warning to me?
warning CS0184: The given expression is never of the provided ('Demo.Base') type.
typeof(whatever) always returns an instance of type Type. Type doesn't derive from Base.
What you want is this:
if(typeof(T) == typeof(Base))
throw new NotSupportedException("Please specify a type derived from Base");
Something that looks like it is the same is this:
if(variableOfTypeT is Base)
But that has a different meaning.
The first statement (with typeof(Base)) only is true if T is Base. It will be false for any type derived from Base.
The second statement (variableOfTypeT is Base) is always true in your class, because any class derived from Base will return true for a check for its base class.
That's not how you check inheritance.
typeof(T) is of type System.Type, it's not Base. To see if T is derived from Base you should use the IsSubclassOf method, like this:
if(typeof(T).IsSubclassOf(typeof(Base)) ...

Can't cast derived type to base abstract class with type parameter

I have a simple factory method which provides a concrete implementation instance based on a generic type parameter provided. If the concrete classes inherit from a common abstract base class with a type parameter I cannot cast them. The compiler tells me Error 2 Cannot convert type 'Car' to 'VehicleBase<T>'. It works fine if I substitute the abstract class for an interface with the same type parameter, or if I remove the generic type parameter from the abstract class.
interface IWheel
{
}
class CarWheel : IWheel
{
}
abstract class VehicleBase<T>
{
}
class Car : VehicleBase<CarWheel>
{
}
class VehicleFactory
{
public static VehicleBase<T> GetNew<T>()
{
if (typeof(T) == typeof(CarWheel))
{
return (VehicleBase<T>)new Car();
}
else
{
throw new NotSupportedException();
}
}
}
This fails to compile on (VehicleBase<T>)new Car(). Is this a compiler defect, or could this be a deliberate design decision to treat abstract classes and interfaces with type parameters differently?
As a workaround I can always make the abstract class implement an interface and use this as the return value for my factory method, but I'd still like to know why this behavior is happening.
That is not provable, because generic code needs to work (with the same IL) for every possible T, and there is nothing to say that Car : VehicleBase<float>, for example. The compiler does not over-analyse the fact that the if check sows that T is CarWheel - the static checker treats each statement separately, it doesn't try to understand the cause-and-effect of conditions.
To force it, cast to object in the middle:
return (VehicleBase<T>)(object)new Car();
However! Your approach isn't really "generic" as such.
This is neither a compiler defect nor a deliberate decision. Type parameters on generic classes are neither covariant nor contravariant, ie there is no inheritance relationship between specializations of the same generic class. From the docs:
In the .NET Framework version 4, variant type parameters are restricted to generic interface and generic delegate types.
Which means that the following code will compile, because it uses an interface instead of an abstract class:
interface IWheel
{
}
class CarWheel : IWheel
{
}
interface IVehicleBase<T>
{
}
class Car : IVehicleBase<CarWheel>
{
}
class VehicleFactory
{
public static IVehicleBase<T> GetNew<T>()
{
if (typeof(T) == typeof(CarWheel))
{
return (IVehicleBase<T>)new Car();
}
else
{
throw new NotSupportedException();
}
}
}
Check "Covariance and Contravariance in Generics" for more info and examples.
There is also a Covariance and Contravariance FAQ at the C# FAQ blog with more info, and an 11-part series! on the subject by Eric Lippert
This seems to work:
return new Car() as VehicleBase<T>;
My guess why it is that way:
As generic type instances of VehicleBase<T> are not related, it cannot be proven that casting them could work:
If T is of type Blah, the cast would not work. You can't go back to object and then take the other branch (there's no multiple inheritance in C# after all).
By casting it back to object before, you are again opening the possibility that the cast might work, because there might still be a path down to VehicleBase<CarWheel>. An interface, of course, can appear anywhere in this tree below object, so that should work, too.

What's the point of having constraints for type parameters in .NET (base class and interface constraints)

I'm working on a small class library at work, and it naturally involves using generics for this task. But there is this thing that I don't really understand with generics:
Why would I need to use generic type parameters, and then constrain the the type parameter to a specific base class or interface.
Here's an example to what I mean:
public class MyGenericClass<T> where T : SomeBaseClass
{
private T data;
}
And here's the implementation without generics
public class MyClass
{
private SomeBaseClass data;
}
Are these two definitions the same (if yes, then i don't see the advatage of using generics here)?
If not, what do we benefit from using generics here?
As with almost all uses of generics, the benefit comes to the consumer. Constraining the type gives you the same advantages that you get by strongly typing your parameter (or you can do other things like ensure that there's a public parameterless constructor or ensure that it's either a value or reference type) while still retaining the niceties of generics for the consumer of your class or function.
Using generics also, for example, allows you to obtain the actual type that was specified, if that's of any particular value.
This example is a little contrived, but look at this:
public class BaseClass
{
public void FunctionYouNeed();
}
public class Derived : BaseClass
{
public void OtherFunction();
}
public class MyGenericClass<T> where T: BaseClass
{
public MyGenericClass(T wrappedValue)
{
WrappedValue = wrappedValue;
}
public T WrappedValue { get; set; }
public void Foo()
{
WrappedValue.FunctionYouNeed();
}
}
...
var MyGenericClass bar = new MyGenericClass<Derived>(new Derived());
bar.Foo();
bar.WrappedValue.OtherFunction();
The difference is that the former defines the new class as a specific type; the latter simply defines a plain class with a field of that type.
It's all about type safety. Using generics you can return a concrete type (T) instead of some base type which defines the API you need in your generic class. Therefore, the caller of your method won't have to cast the result to the concrete type (which is an error-prone operation).
The main difference is in usage. In the first case, the usage can have:
MyGenericClass<SomeDerivedClass> Variable
Variable.data.SomeDerivedProperty = X
And so that when you use that class, you can still access anything from SomeDerivedClass without casting back to it.
The second example will not allow this.
MyClass.data = SomeDerivedClassInstance
MyClass.data.SomeDerivedProperty = X //Compile Error
((SomeDerivedClass)MyClass.data).SomeDerivedProperty = X //Ewwwww
You will have to cast back up to the SomeDerivedClass (which is unsafe) to use something specific to the derived class.
I don't think that there is a huge amount of difference except that the generic version is constraining your Class, whereas the second is just a constraint on a member of the class. If you added more members and methods to your first Class, you would have the same constraint in place.

Categories

Resources