C# Nested generics treated differently when using constraints - c#

When using nested generics, the compiler fails when used directly yet compiles correctly when using constraints.
Example:
public static void Test1<V, E>(this Dictionary<V, E> dict)
where V : IVertex
where E : IEdge<V>
{}
public static void Test2(this Dictionary<IVertex, IEdge<IVertex>> dict){}
The two extension methods above have ostensibly the same signature, but if I was to now try to run code such as:
var dict = new Dictionary<VertexInstance, EdgeInstance>();
dict.Test1();
dict.Test2();
the compiler would err on 'Test2' stating that it could not convert to the generic form with the inline nested generic. Personally I find the syntax for Test2 to be more intuitive.
I posted this originally as an answer in a question which asked about differences between using generic constraints and using interfaces directly, but I am curious as to why this happens?

Expanding my comment:
These extension methods of course do not have the same signature. Dictionary<IVertex, IEdge<IVertex>> is not the same as Dictionary<VertexInstance, EdgeInstance>. #Payo is correct; this is a question of variance. Classes cannot be co- or contravariant. Interfaces can, but only if they're marked for it, and IDictionary cannot be marked for it because it wouldn't be safe, so changing to IDictionary wouldn't help here.
Consider if this were allowed. Your Test2 implementation could look like this:
public static void Test2(this Dictionary<IVertex, IEdge<IVertex>> dict)
{
dict.Add(new EvilVertex(), new EvilEdge());
}
EvilVertex and EvilEdge could implement the correct interfaces, but not inherit from VertexInstance and EdgeInstance. The call would then fail at runtime. The call to Test2 is therefore not provably safe, so the compiler does not allow it.
Thank you for the answer; however, the constraints version could have the same code inside and would have the same issue, wouldn't it?
No! The constraints version could not have the same code inside, because you can't convert from EvilVertex to V, nor from EvilEdge to E. You could force a cast from the type to the type parameter, by casting first to object, but that would of course fail at run time.
Also, why is variance controlled at that level?
Because one purpose of generics is to prove the code's type safety at compile time.
Your dict.Add should have the compilation error not the extension method in my view.
As mentioned, the call to dict Add is a compiler error for the generic version. It can't be a compiler error for the interface version, because in the context of Test2, all you know is that you're converting EvilVertex to IVertex, which is perfectly legal.

Related

Implicit type discovery for generics

I was wondering if C# supported implicit type discovery for class generics.
For example, such functionaly exists on method generics.
I can have the following method:
public void Foo<T>(T obj);
And call it like this:
int n = 0;
instance.Foo(n);
As you can see, I'm not specifying the <int> generic constraint. It's being implicitly discovered, because I passed an int value.
I want to accomplish something similiar on a class definition level:
internal interface IPersistenceStrategy<E, T> : IDisposable
where E : UniqueEntity<T>
I want it to be defined as IPersistenceStrategy<MyEntity>, where MyEntity is an UniqueEntity<int>.
As you can see, the T type param, is being implicitly discovered from MyEntity.
However, this does not work. I have to supply the T param explicitly:
IPersistenceStrategy<MyEntity, int> myStrategy;
Why is this functionality not working? Is C# compiler not smart enough to discover my type param automatically?
Is there some way to accomplish what I am looking for?
There is no type inference in generic type declarations on initialization. You can only omit the generic argument when calling a generic method but it is not the case with initializing a generic type for example:
var list = new List { 2, 3, 4 };
Here you may expect compiler to see that you wanna create a list of int so there is no need to specify type argument.But it is not the case.
In your specific example let's assume compiler has inferred this :
IPersistenceStrategy<MyEntity> myStrategy;
as IPersistenceStrategy<MyEntity,int> then what should happen if there is another declaration in the same assembly such as:
interface IPersistenceStrategy<T> { }
Ofcourse this would cause an ambiguity. So that might be the one of the reasons why it is not allowed.
C# has type inference for methods, but not for constructors. This feature was proposed to be in C# 6 version, but seems was removed from release according to Mads Torgersen (http://blogs.msdn.com/b/csharpfaq/archive/2014/11/20/new-features-in-c-6.aspx).
Also have a look to Languages features in C# 6 and VB 14, i.e. there is no mention about it

Microsoft.VisualStudio.TestTools.UnitTesting.Assert generic method overloads behavior

In the Microsoft.VisualStudio.TestTools.UnitTesting namespace, there is the handy static class Assert to handle the assertions going on in your tests.
Something that has got me bugged is that most methods are extremely overloaded, and on top of that, they have a generic version. One specific example is Assert.AreEqual which has 18 overloads, among them:
Assert.AreEqual<T>(T t1, T t2)
What is the use of this generic method? Originally, I thought this was a way to directly call the IEquatable<T> Equals(T t) method, but that is not the case; it will always call the non-generic version object.Equals(object other). I found out the hard way after coding quite a few unit tests expecting that behavior (instead of examining the Assert class definition beforehand like I should have).
In order to call the generic version of Equals, the generic method would had to be defined as:
Assert.AreEqual<T>(T t1, T t2) where T: IEquatable<T>
Is there a good reason why it wasn't done this way?
Yes, you loose the generic method for all those types that don't implement IEquatable<T>, but it's not a great loss anyway as equality would be checked through object.Equals(object other), so Assert.AreEqual(object o1, object o2) is already good enough.
Does the current generic method offer advantages I'm not considering, or is it just the case that no one stopped to think about it as it's not that much of a deal? The only advantage I see is argument type safety, but that seems kind of poor.
Edit: fixed an error where I kept referring to IComparable when I meant IEquatable.
The method having that constraint would be non-ideal because of the oft-faced problem of constraints not being part of the signature.
The issue would be that for any T that is not covered by its own specific overload, the compiler would choose the generic AreEqual<T> method as the best fit during overload resolution, as it would indeed by an exact match. In a different step of the process, the compiler would evaluate that T passes the constraint. For any T that does not implement IEquatable<T>, this check would fail and the code would not compile.
Consider this simplified example of the unit testing library code and a class that might exist in your library:
public static class Assert
{
public static void AreEqual(object expected, object actual) { }
public static void AreEqual<T>(T expected, T actual) where T : IEquatable<T> { }
}
class Bar { }
Class Bar does not implement the interface in the constraint. If we were then to add the following code to a unit test
Assert.AreEqual(new Bar(), new Bar());
The code would fail to compile because of the unsatisfied constraint on the method that is the best candidate. (Bar substitutes for T, which makes it a better candidate than object.)
The type 'Bar' cannot be used as type parameter 'T' in the generic type or method 'Assert.AreEqual<T>(T, T)'. There is no implicit reference conversion from 'Bar' to 'System.IEquatable<Bar>'.
In order to satisfy the compiler and allow our unit test code to compile and run, we would have to cast at least one input to the method to object so that the non-generic overload can be chosen, and this would be true for any given T that might exist in your own code or code you consume that you wish to use in your test cases that does not implement the interface.
Assert.AreEqual((object)new Bar(), new Bar());
So the question must be asked -- would that be ideal? If you were writing a unit testing library, would you create such a method with such an unfriendly limitation? I suspect you would not, and the implementers of the Microsoft unit testing library (whether it was for this reason or not) did not either.
Basically, the generic overload force you to compare two objects of same type. In case you change type of expecting or actual value, the compilation error will appear. Here is MSDN blog describing it quite well.
You can decompile the method and see that all it really does is add a type check (via ILSpy), which isn't even done correctly in my opinion (it checks types after equality):
public static void AreEqual<T>(T expected, T actual, string message, params object[] parameters)
{
message = Assert.CreateCompleteMessage(message, parameters);
if (!object.Equals(expected, actual))
{
string message2;
if (actual != null && expected != null && !actual.GetType().Equals(expected.GetType()))
{
message2 = FrameworkMessages.AreEqualDifferentTypesFailMsg((message == null) ? string.Empty : Assert.ReplaceNulls(message), Assert.ReplaceNulls(expected), expected.GetType().FullName, Assert.ReplaceNulls(actual), actual.GetType().FullName);
}
else
{
message2 = FrameworkMessages.AreEqualFailMsg((message == null) ? string.Empty : Assert.ReplaceNulls(message), Assert.ReplaceNulls(expected), Assert.ReplaceNulls(actual));
}
Assert.HandleFailure("Assert.AreEqual", message2);
}
}
Theoretically, it could use EqualityComparer<T>.Default to use the generic Equals, if available, but which would fallback to non-generic Equals otherwise. This would then not require a constraint to IEquatable<T>. Having a different behavior for the generic and non-generic Equals methods are a code smell, IMO.
Honestly, the generic overload is not tremendously useful unless you type out the generic parameter. I cannot count how many times I've mistyped a property or compared two properties that are different types and it picked the AreEqual(object,object) overload. Thus giving me a failure much later at run time rather than compile time.

Using generic constraints with value types

I am experimenting with fluent extension methods.
I have the following simple extension method to perform a safe cast.
public static T As<T>(this Object source)
where T : class
{
return source as T;
}
This worked well, but when I tried to make it intuitive to use valuetypes with an overload
public static T As<T>(this ValueType source)
where T : struct
{
return (T)source;
}
I ran into problems. The method resolution logic always chooses the first method above, and gives a syntax error (accurately) that the struct is not a class.
Is there a way to handle the above, or should I go the route of removing the constraint while testing for and handling all types in the same method?
==== Edit: to answer questions ====
I am compiling this against the 3.5 framework. I'm not really trying to accomplish anything in particular; this is just an experiment with the above. My interest was piqued and I threw together some code.
I'm not particularly concerned with it remaining a 'safe' cast. That is how it started, and can be kept safe with default() -- but that's not really the focus of the question and code to ensure 'safeness' would just obscure.
As to the expressiveness, no value.As<int>() is not any more expressive than (int)value; but why should the user of the method have to 'just know' it only works with reference types? My trying to work it in was more about the expected behavior of the method than expressive writing.
The code snippet value.As<DateTime>(), gives the error "The type 'System.DateTime' must be a reference type in order to use it as parameter 'T' in the generic type or method ....As(object)". From the error message I see it is resolving to use the top method above as it is the one requiring the reference type.
In .NET 4, the second overload is chosen based on your code sample. (Also just tested against .NET 3.5, same result.)
int myInt = 1;
long myLong = myInt.As<long>(); // chooses ValueType version
However, this only gets us to the scene of the next crash. The (T)source; results in an invalid cast exception. You could get around that by writing the method as
public static T As<T>(this ValueType source)
where T : struct
{
return (T)Convert.ChangeType(source, typeof(T));
}
However, I wonder what you're actually looking to achieve, as I do not see the immediate benefit. (And for that matter, this isn't safe like the source as T object version.) For example, how is
long myLong = myInt.As<long>();
Any more expressive or easier to use than
long myLong = (long)myInt;

How to do a static cast in C#?

Given a couple types like this:
interface I {}
class C : I {}
How can I do a static type cast? By this I mean: how can I change its type in a way that gets checked at compile time?
In C++ you can do static_cast<I*>(c). In C# the best I can do is create a temporary variable of the alternate type and try to assign it:
var c = new C();
I i = c; // statically checked
But this prevents fluent programming. I have to create a new variable just to do the type check. So I've settled on something like this:
class C : I
{
public I I { get { return this; } }
}
Now I can statically convert C to I by just calling c.I.
Is there a better way to do this in C#?
(In case anyone's wondering why I want to do this, it's because I use explicit interface implementations, and calling one of those from within another member function requires a cast to the interface type first, otherwise the compiler can't find the method.)
UPDATE
Another option I came up with is an object extension:
public static class ObjectExtensions
{
[DebuggerStepThrough]
public static T StaticTo<T>(this T o)
{
return o;
}
}
So ((I)c).Doit() could also be c.StaticTo<I>().Doit(). Hmm...probably will still stick with the simple cast. Figured I'd post this other option anyway.
Simply cast it:
(I)c
Edit Example:
var c = new C();
((I)c).MethodOnI();
Write an extension method that uses the trick you mentioned in your UPDATE:
public static class ObjectExtensions
{
public static T StaticCast<T>(this T o) => o;
}
To use:
things.StaticCast<IEnumerable>().GetEnumerator();
If things is, e.g., IEnumerable<object>, this compiles. If things is object, it fails.
// Compiles (because IEnumerable<char> is known at compiletime
// to be IEnumerable too).
"adsf".StaticCast<IEnumerable>().GetEnumerator();
// error CS1929: 'object' does not contain a definition for 'StaticCast'
// and the best extension method overload
// 'ObjectExtensions.StaticCast<IEnumerable>(IEnumerable)'
// requires a receiver of type 'IEnumerable'
new object().StaticCast<IEnumerable>().GetEnumerator();
Why Use a Static Cast?
One common practice during refactoring is to go ahead and make your changes and then verify that your changes have not caused any regressions. You can detect regressions in various ways and at various stages. For example, some types of refactoring may result in API changes/breakage and require refactoring other parts of the codebase.
If one part of your code expects to receive a type (ClassA) that should be known at compiletime to implement an interface (IInterfaceA) and that code wants to access interface members directly, it may have to cast down to the interface type to, e.g., access explicitly implemented interface members. If, after refactoring, ClassA no longer implements IIterfaceA, you get different types of errors depending on how you casted down to the interface:
C-style cast: ((IInterfaceA)MethodReturningClassA()).Act(); would suddenly become a runtime cast and throw a runtime error.
Assigning to an explicitly-typed variable: IInterfaceA a = MethodReturningClassA(); a.Act(); would raise a compiletime error.
Using the static_cast<T>-like extension method: MethodReturningClassA().StaticCast<IInterfaceA>().Act(); would raise a compiletime error.
If you expected your cast to be a downcast and to be verifiable at compiletime, then you should use a casting method that forces compiletime verification. This makes the intentions of the code’s original developer to write typesafe code clear. And writing typesafe code has the benefit of being more verifiable at compiletime. By doing a little bit of work to clarify your intention to opt into typesafety to both other developers, yourself, and the compiler, you magically get the compiler’s help in verifying your code and can catch repercussions of refactoring earlier (at compiletime) than later (such as a runtime crash if your code didn’t happen to have full test coverage).
var c = new C();
I i = c; // statically checked
equals to
I i = new C();
If you're really just looking for a way to see if an object implements a specific type, you should use as.
I i = whatever as i;
if (i == null) // It wasn't
Otherwise, you just cast it. (There aren't really multiple types of casting in .NET like there are in C++ -- unless you get deeper than most people need to, but then it's more about WeakReference and such things.)
I i = (I)c;
If you're just looking for a convenient way to turn anything implementing I into an I, then you could use an extension method or something similar.
public static I ToI(this I #this)
{
return #this;
}

Why it isn't possible to declare a method parameter as var type

I wonder why it is not possible a method parameter as var type like
private void myMethod(var myValue) {
// do something
}
You can only use var for variables inside the method body. Also the variable must be assigned at declaration and it must be possible to deduce the type unambiguously from the expression on the right-hand side.
In all other places you must specify a type, even if a type could in theory be deduced.
The reason is due to the way that the compiler is designed. A simplified description is that it first parses everything except method bodies and then makes a full analysis of the static types of every class, member, etc. It then uses this information when parsing the method bodies, and in particular for deducing the type of local variables declared as var. If var were allowed anywhere then it would require a large change to the way the compiler works.
You can read Eric Lippert's article on this subject for more details:
Why no var on fields?
Because the compiler determines the actual type by looking at the right hand side of the assignment. For example, here it is determined to be a string:
var s = "hello";
Here it is determined to be Foo:
var foo = new Foo();
In method arguments, there is no "right hand side of the assignment", so you can't use var.
See the posting by Eric Lippert about why var is not allowed on fields, which also contains the explanation why it doesn't work in method signatures:
Let me give you a quick oversimplification of how the C# compiler works. First we run through every source file and do a "top level only" parse. That is, we identify every namespace, class, struct, enum, interface, and delegate type declaration at all levels of nesting. We parse all field declarations, method declarations, and so on. In fact, we parse everything except method bodies; those, we skip and come back to them later.
[...]
if we have "var" fields then the type of the field cannot be determined until the expression is analyzed, and that happens after we already need to know the type of the field.
Please see Juliet's answer for a better answer to this question.
Because it was too hard to add full type inference to C#.
Other languages such as Haskell and ML can automatically infer the most general type without you having to declare it.
The other answers state that it's "impossible" for the compiler to infer the type of var but actually it is possible in principle. For example:
abstract void anotherMethod(double z, double w);
void myMethod<T>(T arg)
{
anotherMethod(arg, 2.0); // Now a compiler could in principle infer that arg must be of type double (but the actual C# compiler can't)
}
Have "var" method parameters is in principle the same thing as generic methods:
void myMethod<T>(T arg)
{
....
}
It is unfortunate that you can't just use the same syntax for both but this is probably due to the fact that that C#'s type inference was added only later.
In general, subtle changes in the language syntax and semantics can turn a "deterministic" type inference algorithm into an undecidable one.
ML, Haskell, Scala, F#, SML, and other languages can easily figure out the type from equivalent expressions in their own language, mainly because they were designed with type-inference in mind from the very start. C# wasn't, its type-inference was tacked on as a post-hoc solution to the problem of accessing anonymous types.
I speculate that true Hindley-Milner type-inference was never implemented for C# because its complicated to deduce types in a language so dependent on classes and inheritance. Let's say I have the following classes:
class Base { public void Print() { ... } }
class Derived1 : Base { }
class Derived2 : Base { }
And now I have this method:
var create() { return new Derived1(); }
What's the return type here? Is it Derived1, or should it be Base? For that matter, should it be object?
Ok, now lets say I have this method:
void doStuff(var someBase) { someBase.Print(); }
void Main()
{
doStuff(new Derived1());
doStuff(new Derived2()); // <-- type error or not?
}
The first call, doStuff(new Derived1()), presumably forces doStuff to the type doStuff(Derived1 someBase). Let's assume for now that we infer a concrete type instead of a generic type T.
What about the second call, doStuff(new Derived1())? Is it a type error, or do we generalize to doStuff<T>(T somebase) where T : Base instead? What if we made the same call in a separate, unreferenced assembly -- the type inference algorithm would have no idea whether to use the narrow type or the more genenarlized type. So we'd end up with two different type signatures based on whether method calls originate from inside the assembly or a foreign assembly.
You can't generalize wider types based on usage of the function. You basically need to settle on a single concrete type as soon as you know which concrete type is being pass in. So in the example code above, unless you explicitly cast up to the Base type, doStuff is constrained to accept types of Derived1 and the second call is a type error.
Now the trick here is settling on a type. What happens here:
class Whatever
{
void Foo() { DoStuff(new Derived1()); }
void Bar() { DoStuff(new Derived2()); }
void DoStuff(var x) { ... }
}
What's the type of DoStuff? For that matter, we know based on the above that one of the Foo or Bar methods contain a type error, but can you tell from looking which has the error?
Its not possible to resolve the type without changing the semantics of C#. In C#, order of method declaration has no impact on compilation (or at least it shouldn't ;) ). You might say instead that the method declared first (in this case, the Foo method) determines the type, so Bar has an error.
This works, but it also changes the semantics of C#: changes in method order will change the compiled type of the method.
But let's say we went further:
// Whatever.cs
class Whatever
{
public void DoStuff(var x);
}
// Foo.cs
class Foo
{
public Foo() { new Whatever().DoStuff(new Derived1()); }
}
// Bar.cs
class Bar
{
public Bar() { new Whatever().DoStuff(new Derived2()); }
}
Now the methods is being invoked from different files. What's the type? Its not possible to decide without imposing some rules on compilation order: if Foo.cs gets compiled before Bar.cs, the type is determined by Foo.cs.
While we can impose those sorts of rules on C# to make type inference work, it would drastically change the semantics of the language.
By contrast, ML, Haskell, F#, and SML support type inference so well because they have these sorts of restrictions: you can't call methods before they're declared, the first method call to inferred functions determines the type, compilation order has an impact on type inference, etc.
The "var" keyword is used in C# and VB.NET for type inference - you basically tell the C# compiler: "you figure out what the type is".
"var" is still strongly typed - you're just too lazy yourself to write out the type and let the compiler figure it out - based on the data type of the right-hand side of the assignment.
Here, in a method parameter, the compiler has no way of figuring out what you really meant. How? What type did you really mean? There's no way for the compiler to infer the type from the method definition - therefore it's not a valid statement.
Because c# is type safe and strong type language. At any place of your program compiler always knows the type of argument you are using. var keyword was just introduced to have variables of anonymus types.
Check dynamic in C# 4
Type inference is type inference, either in local expressions or global / interprocedural. So it isn't about "not having a right hand side", because in compiler theory, a procedure call is a form of "right hand side".
C# could do this if the compiler did global type inference, but it does not.
You can use "object" if you want a parameter that accepts anything, but then you need to deal with the runtime conversion and potential exceptions yourself.
"var" in C# isn't a runtime type binding, it is a compile time feature that ends up with a very specific type, but C# type inference is limited in scope.

Categories

Resources