//version1
interface eer
{
public abstract void S1<T>(T? t) where T : struct;
public abstract void S1<T>(T? t) ; //ok
}
//version2
interface eer
{
public abstract void S1<T>(T t) where T : struct;
public abstract void S1<T>(T t) ; //error
}
As you can see when I use nullable it is okay to have the 2 methods but when I do not use nullable, it will not compile.
Could you please tell me exactly why this is so with an example?
It is because of nullable reference types and how signatures of methods are generated in C#:
T?
is a value type with where T : struct and
a reference type without the where.
In C# method signatures
include type of the parameter (value, reference, or output)
don't include type parameter constraints.
This means that both T versions (S1<T>(T t)) generate the same method signature because the constraint is not part of it.
In C# 7.0 draft specification in 7.6 Signatures and overloading¹ we can read:
7.6 Signatures and overloading
The signature of a method consists of the name of the method, the number of type parameters, and the type and parameter-passing mode (value, reference, or output) of each of its formal parameters, considered in the order left to right. For these purposes, any type parameter of the method that occurs in the type of a formal parameter is identified not by its name, but by its ordinal position in the type parameter list of the method. The signature of a method specifically does not include the return type, parameter names, type parameter names, type parameter constraints, the params or this parameter modifiers, nor whether parameters are required or optional.
[emphasis mine]
BTW. A related problem (not just for overloading, but also for overriding) is described in where (generic type constraint) (C# Reference) in the section about overriding:
The addition of nullable reference types introduces a potential ambiguity in the meaning of T? in generic methods. If T is a struct, T? is the same as System.Nullable. However, if T is a reference type, T? means that null is a valid value. The ambiguity arises because overriding methods can't include constraints. The new default constraint resolves this ambiguity. You'll add it when a base class or interface declares two overloads of a method, one that specifies the struct constraint, and one that doesn't have either the struct or class constraint applied:
public abstract class B
{
public void M<T>(T? item) where T : struct { }
public abstract void M<T>(T? item);
}
You use the default constraint to specify that your derived class overrides the method without the constraint in your derived class, or explicit interface implementation. It's only valid on methods that override base methods, or explicit interface implementations:
public class D : B
{
// Without the "default" constraint, the compiler tries to override the first method in B
public override void M<T>(T? item) where T : default { }
}
¹ - Available also here from https://github.com/dotnet/csharpstandard
Related
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();
}
}
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;
}
public interface PipelineElement<in TIn, out TOut>
{
IEnumerable<TOut> Run(IEnumerable<TIn> input, Action<Error> errorReporter);
}
public interface Stage
{
}
public abstract class PipelineElementBase<TIn, TOut> : PipelineElement<object, object>,
PipelineElement<TIn, TOut> where TIn : Stage where TOut : Stage
{
IEnumerable<object> PipelineElement<object, object>.Run(IEnumerable<object> input, Action<Error> errorReporter)
{
return this.Run(input.Cast<TIn>(), errorReporter).Cast<object>();
}
public abstract IEnumerable<TOut> Run(IEnumerable<TIn> input, Action<Error> errorReporter);
}
object doesn't implement Stage, therefore neither TIn nor TOut could ever be object, right? So why does the compiler think that PipelineElement<object, object> and PipelineElement<TIn, TOut> can become identical?
EDIT: Yes, it is perfectly possible to implement the same generic interface multiple times:
public interface MyInterface<A> { }
public class MyClass: MyInterface<string>, MyInterface<int> { }
From Compiler Error CS0695
'generic type' cannot implement both 'generic interface' and 'generic
interface' because they may unify for some type parameter
substitutions.
This error occurs when a generic class implements more than one
parameterization of the same generic interface, and there exists a
type parameter substitution which would make the two interfaces
identical. To avoid this error, implement only one of the interfaces,
or change the type parameters to avoid the conflict.
You can't implement both PipelineElementBase<TIn, TOut> and PipelineElement<object, object> interfaces to your abstract class.
As the error page said, you should;
Implement only one of these or
Change the type parameters to avoid the conflict.
From C# 5.0 Language Specification
13.4.2 Uniqueness of implemented interfaces
The interfaces implemented by a generic type declaration must remain
unique for all possible constructed types. Without this rule, it would
be impossible to determine the correct method to call for certain
constructed types. For example, suppose a generic class declaration
were permitted to be written as follows:
interface I<T>
{
void F();
}
class X<U,V>: I<U>, I<V>
{
void I<U>.F() {...}
void I<V>.F() {...}
}
Were this permitted, it would be impossible to determine which code to
execute in the following case:
I<int> x = new X<int,int>();
x.F();
To determine if the interface list of a generic type declaration is
valid, the following steps are performed:
Let L be the list of interfaces directly specified in a generic class, struct, or interface declaration C.
Add to L any base interfaces of the interfaces already in L.
Remove any duplicates from L.
If any possible constructed type created from C would, after type arguments are substituted into L, cause two interfaces in L to be
identical, then the declaration of C is invalid. Constraint
declarations are not considered when determining all possible
constructed types.
In the class declaration X above, the interface list L consists of
I<U> and I<V>. The declaration is invalid because any constructed
type with U and V being the same type would cause these two
interfaces to be identical types.
It is possible for interfaces specified at different inheritance
levels to unify:
interface I<T>
{
void F();
}
class Base<U>: I<U>
{
void I<U>.F() {…}
}
class Derived<U,V>: Base<U>, I<V> // Ok
{
void I<V>.F() {…}
}
This code is valid even though Derived<U,V> implements both I<U>
and I<V>. The code
I<int> x = new Derived<int,int>();
x.F();
invokes the method in Derived, since Derived<int,int> effectively
re-implements I<int>(§13.4.6).
[Emphasis by the SO editor.]
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)) ...
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