Problem understanding covariance contravariance with generics in C#











up vote
95
down vote

favorite
13












I can't understand why the following C# code doesn't compile.



As you can see, I have a static generic method Something with an IEnumerable<T> parameter (and T is constrained to be an IA interface), and this parameter can't be implicitly converted to IEnumerable<IA>.



What is the explanation? (I don't search for a workaround, just to understand why it doesn't work).



public interface IA { }
public interface IB : IA { }
public class CIA : IA { }
public class CIAD : CIA { }
public class CIB : IB { }
public class CIBD : CIB { }

public static class Test
{
public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA
{
var bar = foo.ToList();

// All those calls are legal
Something2(new List<IA>());
Something2(new List<IB>());
Something2(new List<CIA>());
Something2(new List<CIAD>());
Something2(new List<CIB>());
Something2(new List<CIBD>());
Something2(bar.Cast<IA>());

// This call is illegal
Something2(bar);

return bar;
}

private static void Something2(IEnumerable<IA> foo)
{
}
}


Error I get in Something2(bar) line:



 Argument 1: cannot convert from 'System.Collections.Generic.List<T>'
to 'System.Collections.Generic.IEnumerable<ConsoleApp20.Program.IA>'









share|improve this question




















  • 8




    Possible duplicate of Why covariance and contravariance do not support value type
    – Dirk
    Nov 7 at 8:40






  • 11




    You have not restricted T to reference types. If you use the condition where T: class, IA then it should work. The linked answer has more details.
    – Dirk
    Nov 7 at 8:41






  • 2




    @Dirk I don't think this should be flagged as a duplicate. While it's true that the concept problem here is a covariance/contravariance problem in the face of value types, the specific case here is "what does this error message mean" as well as the author not realizing merely including "class" fixes his issue. I believe future users will search for this error message, find this post, and leave happy. (As I often do.)
    – Reginald Blue
    Nov 7 at 13:46










  • You can also reproduce the situation by simply saying Something2(foo); directly. Going around .ToList() to get a List<T> (T is your type parameter declared by the generic method) is not needed to understand this (a List<T> is an IEnumerable<T>).
    – Jeppe Stig Nielsen
    Nov 8 at 12:18










  • @ReginaldBlue 100%, was going to post the same thing. Similar answers do not a duplicate question make.
    – DaveInCaz
    Nov 11 at 13:35















up vote
95
down vote

favorite
13












I can't understand why the following C# code doesn't compile.



As you can see, I have a static generic method Something with an IEnumerable<T> parameter (and T is constrained to be an IA interface), and this parameter can't be implicitly converted to IEnumerable<IA>.



What is the explanation? (I don't search for a workaround, just to understand why it doesn't work).



public interface IA { }
public interface IB : IA { }
public class CIA : IA { }
public class CIAD : CIA { }
public class CIB : IB { }
public class CIBD : CIB { }

public static class Test
{
public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA
{
var bar = foo.ToList();

// All those calls are legal
Something2(new List<IA>());
Something2(new List<IB>());
Something2(new List<CIA>());
Something2(new List<CIAD>());
Something2(new List<CIB>());
Something2(new List<CIBD>());
Something2(bar.Cast<IA>());

// This call is illegal
Something2(bar);

return bar;
}

private static void Something2(IEnumerable<IA> foo)
{
}
}


Error I get in Something2(bar) line:



 Argument 1: cannot convert from 'System.Collections.Generic.List<T>'
to 'System.Collections.Generic.IEnumerable<ConsoleApp20.Program.IA>'









share|improve this question




















  • 8




    Possible duplicate of Why covariance and contravariance do not support value type
    – Dirk
    Nov 7 at 8:40






  • 11




    You have not restricted T to reference types. If you use the condition where T: class, IA then it should work. The linked answer has more details.
    – Dirk
    Nov 7 at 8:41






  • 2




    @Dirk I don't think this should be flagged as a duplicate. While it's true that the concept problem here is a covariance/contravariance problem in the face of value types, the specific case here is "what does this error message mean" as well as the author not realizing merely including "class" fixes his issue. I believe future users will search for this error message, find this post, and leave happy. (As I often do.)
    – Reginald Blue
    Nov 7 at 13:46










  • You can also reproduce the situation by simply saying Something2(foo); directly. Going around .ToList() to get a List<T> (T is your type parameter declared by the generic method) is not needed to understand this (a List<T> is an IEnumerable<T>).
    – Jeppe Stig Nielsen
    Nov 8 at 12:18










  • @ReginaldBlue 100%, was going to post the same thing. Similar answers do not a duplicate question make.
    – DaveInCaz
    Nov 11 at 13:35













up vote
95
down vote

favorite
13









up vote
95
down vote

favorite
13






13





I can't understand why the following C# code doesn't compile.



As you can see, I have a static generic method Something with an IEnumerable<T> parameter (and T is constrained to be an IA interface), and this parameter can't be implicitly converted to IEnumerable<IA>.



What is the explanation? (I don't search for a workaround, just to understand why it doesn't work).



public interface IA { }
public interface IB : IA { }
public class CIA : IA { }
public class CIAD : CIA { }
public class CIB : IB { }
public class CIBD : CIB { }

public static class Test
{
public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA
{
var bar = foo.ToList();

// All those calls are legal
Something2(new List<IA>());
Something2(new List<IB>());
Something2(new List<CIA>());
Something2(new List<CIAD>());
Something2(new List<CIB>());
Something2(new List<CIBD>());
Something2(bar.Cast<IA>());

// This call is illegal
Something2(bar);

return bar;
}

private static void Something2(IEnumerable<IA> foo)
{
}
}


Error I get in Something2(bar) line:



 Argument 1: cannot convert from 'System.Collections.Generic.List<T>'
to 'System.Collections.Generic.IEnumerable<ConsoleApp20.Program.IA>'









share|improve this question















I can't understand why the following C# code doesn't compile.



As you can see, I have a static generic method Something with an IEnumerable<T> parameter (and T is constrained to be an IA interface), and this parameter can't be implicitly converted to IEnumerable<IA>.



What is the explanation? (I don't search for a workaround, just to understand why it doesn't work).



public interface IA { }
public interface IB : IA { }
public class CIA : IA { }
public class CIAD : CIA { }
public class CIB : IB { }
public class CIBD : CIB { }

public static class Test
{
public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA
{
var bar = foo.ToList();

// All those calls are legal
Something2(new List<IA>());
Something2(new List<IB>());
Something2(new List<CIA>());
Something2(new List<CIAD>());
Something2(new List<CIB>());
Something2(new List<CIBD>());
Something2(bar.Cast<IA>());

// This call is illegal
Something2(bar);

return bar;
}

private static void Something2(IEnumerable<IA> foo)
{
}
}


Error I get in Something2(bar) line:



 Argument 1: cannot convert from 'System.Collections.Generic.List<T>'
to 'System.Collections.Generic.IEnumerable<ConsoleApp20.Program.IA>'






c# covariance






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 7 at 19:56









Peter Mortensen

13.3k1983111




13.3k1983111










asked Nov 7 at 8:30









BenLaz

54847




54847








  • 8




    Possible duplicate of Why covariance and contravariance do not support value type
    – Dirk
    Nov 7 at 8:40






  • 11




    You have not restricted T to reference types. If you use the condition where T: class, IA then it should work. The linked answer has more details.
    – Dirk
    Nov 7 at 8:41






  • 2




    @Dirk I don't think this should be flagged as a duplicate. While it's true that the concept problem here is a covariance/contravariance problem in the face of value types, the specific case here is "what does this error message mean" as well as the author not realizing merely including "class" fixes his issue. I believe future users will search for this error message, find this post, and leave happy. (As I often do.)
    – Reginald Blue
    Nov 7 at 13:46










  • You can also reproduce the situation by simply saying Something2(foo); directly. Going around .ToList() to get a List<T> (T is your type parameter declared by the generic method) is not needed to understand this (a List<T> is an IEnumerable<T>).
    – Jeppe Stig Nielsen
    Nov 8 at 12:18










  • @ReginaldBlue 100%, was going to post the same thing. Similar answers do not a duplicate question make.
    – DaveInCaz
    Nov 11 at 13:35














  • 8




    Possible duplicate of Why covariance and contravariance do not support value type
    – Dirk
    Nov 7 at 8:40






  • 11




    You have not restricted T to reference types. If you use the condition where T: class, IA then it should work. The linked answer has more details.
    – Dirk
    Nov 7 at 8:41






  • 2




    @Dirk I don't think this should be flagged as a duplicate. While it's true that the concept problem here is a covariance/contravariance problem in the face of value types, the specific case here is "what does this error message mean" as well as the author not realizing merely including "class" fixes his issue. I believe future users will search for this error message, find this post, and leave happy. (As I often do.)
    – Reginald Blue
    Nov 7 at 13:46










  • You can also reproduce the situation by simply saying Something2(foo); directly. Going around .ToList() to get a List<T> (T is your type parameter declared by the generic method) is not needed to understand this (a List<T> is an IEnumerable<T>).
    – Jeppe Stig Nielsen
    Nov 8 at 12:18










  • @ReginaldBlue 100%, was going to post the same thing. Similar answers do not a duplicate question make.
    – DaveInCaz
    Nov 11 at 13:35








8




8




Possible duplicate of Why covariance and contravariance do not support value type
– Dirk
Nov 7 at 8:40




Possible duplicate of Why covariance and contravariance do not support value type
– Dirk
Nov 7 at 8:40




11




11




You have not restricted T to reference types. If you use the condition where T: class, IA then it should work. The linked answer has more details.
– Dirk
Nov 7 at 8:41




You have not restricted T to reference types. If you use the condition where T: class, IA then it should work. The linked answer has more details.
– Dirk
Nov 7 at 8:41




2




2




@Dirk I don't think this should be flagged as a duplicate. While it's true that the concept problem here is a covariance/contravariance problem in the face of value types, the specific case here is "what does this error message mean" as well as the author not realizing merely including "class" fixes his issue. I believe future users will search for this error message, find this post, and leave happy. (As I often do.)
– Reginald Blue
Nov 7 at 13:46




@Dirk I don't think this should be flagged as a duplicate. While it's true that the concept problem here is a covariance/contravariance problem in the face of value types, the specific case here is "what does this error message mean" as well as the author not realizing merely including "class" fixes his issue. I believe future users will search for this error message, find this post, and leave happy. (As I often do.)
– Reginald Blue
Nov 7 at 13:46












You can also reproduce the situation by simply saying Something2(foo); directly. Going around .ToList() to get a List<T> (T is your type parameter declared by the generic method) is not needed to understand this (a List<T> is an IEnumerable<T>).
– Jeppe Stig Nielsen
Nov 8 at 12:18




You can also reproduce the situation by simply saying Something2(foo); directly. Going around .ToList() to get a List<T> (T is your type parameter declared by the generic method) is not needed to understand this (a List<T> is an IEnumerable<T>).
– Jeppe Stig Nielsen
Nov 8 at 12:18












@ReginaldBlue 100%, was going to post the same thing. Similar answers do not a duplicate question make.
– DaveInCaz
Nov 11 at 13:35




@ReginaldBlue 100%, was going to post the same thing. Similar answers do not a duplicate question make.
– DaveInCaz
Nov 11 at 13:35












2 Answers
2






active

oldest

votes

















up vote
199
down vote



accepted










The error message is insufficiently informative, and that is my fault. Sorry about that.



The problem you are experiencing is a consequence of the fact that covariance only works on reference types.



You're probably saying "but IA is a reference type" right now. Yes, it is. But you didn't say that T is equal to IA. You said that T is a type which implements IA, and a value type can implement an interface. Therefore we do not know whether covariance will work, and we disallow it.



If you want covariance to work you have to tell the compiler that the type parameter is a reference type with the class constraint as well as the IA interface constraint.



The error message really should say that the conversion is not possible because covariance requires a guarantee of reference-type-ness, since that is the fundamental problem.






share|improve this answer



















  • 3




    Why you said it's your fault?
    – user4951
    Nov 7 at 15:04






  • 68




    @user4951: Because I implemented all of the conversion checking logic including the error messages.
    – Eric Lippert
    Nov 7 at 15:06






  • 1




    @PeterA.Schneider: I appreciate that. But one of my primary goals for designing the error reporting logic in Roslyn was in particular to capture not just what rule was violated, but furthermore, to identify the "root cause" where possible. For example, what should the error message be for customers.Select(c=>c.FristName) ? The C# specification is very clear that this is an overload resolution error: the set of applicable methods named Select that can take that lambda is empty. But the root cause is that FirstName has a typo.
    – Eric Lippert
    Nov 8 at 19:39






  • 2




    @PeterA.Schneider: I did a lot of work to ensure that scenarios involving generic type inference and lambdas used the appropriate heuristics to deduce what error message was likely to best help the developer. But I did a far less good job on the conversion error messages, particularly where variance was concerned. I've always regretted that.
    – Eric Lippert
    Nov 8 at 19:40






  • 3




    @EricLippert This is probably not the place to say this but that is why I (and many more like me) always look up to you Eric (always loved the way you explain), thank you for all the hard work and enlightenment and counting++. My sign of respect/appreciation for one of the best in the business :)
    – Rohit416
    Nov 13 at 11:46


















up vote
24
down vote













I just wanted to complement Eric's excellent insider answer with a code example for those that may not be that familiar with generic constraints.



Change Something's signature like this: The class constraint has to come first.



public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA





share|improve this answer



















  • 2




    I'm curious... what exactly is the reason behind the significance of the ordering?
    – Tom Wright
    Nov 8 at 2:11






  • 5




    @TomWright - the spec doesn't, of course, include the answer to many "Why?" questions, but in this case does make it clear that there are three distinct types of constraints, and when all three are used they have to be specifically primary_constraint ',' secondary_constraints ',' constructor_constraint
    – Damien_The_Unbeliever
    Nov 8 at 7:05






  • 1




    @TomWright: Damien is correct; there is no particular reason I'm aware of other than the convenience of the author of the parser. If I had my druthers the syntax for type constraints would be considerably more verbose. class is bad because it means "reference type", not "class". I would have been happier with something verbose like where T is not struct
    – Eric Lippert
    Nov 10 at 0:49











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53185806%2fproblem-understanding-covariance-contravariance-with-generics-in-c-sharp%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
199
down vote



accepted










The error message is insufficiently informative, and that is my fault. Sorry about that.



The problem you are experiencing is a consequence of the fact that covariance only works on reference types.



You're probably saying "but IA is a reference type" right now. Yes, it is. But you didn't say that T is equal to IA. You said that T is a type which implements IA, and a value type can implement an interface. Therefore we do not know whether covariance will work, and we disallow it.



If you want covariance to work you have to tell the compiler that the type parameter is a reference type with the class constraint as well as the IA interface constraint.



The error message really should say that the conversion is not possible because covariance requires a guarantee of reference-type-ness, since that is the fundamental problem.






share|improve this answer



















  • 3




    Why you said it's your fault?
    – user4951
    Nov 7 at 15:04






  • 68




    @user4951: Because I implemented all of the conversion checking logic including the error messages.
    – Eric Lippert
    Nov 7 at 15:06






  • 1




    @PeterA.Schneider: I appreciate that. But one of my primary goals for designing the error reporting logic in Roslyn was in particular to capture not just what rule was violated, but furthermore, to identify the "root cause" where possible. For example, what should the error message be for customers.Select(c=>c.FristName) ? The C# specification is very clear that this is an overload resolution error: the set of applicable methods named Select that can take that lambda is empty. But the root cause is that FirstName has a typo.
    – Eric Lippert
    Nov 8 at 19:39






  • 2




    @PeterA.Schneider: I did a lot of work to ensure that scenarios involving generic type inference and lambdas used the appropriate heuristics to deduce what error message was likely to best help the developer. But I did a far less good job on the conversion error messages, particularly where variance was concerned. I've always regretted that.
    – Eric Lippert
    Nov 8 at 19:40






  • 3




    @EricLippert This is probably not the place to say this but that is why I (and many more like me) always look up to you Eric (always loved the way you explain), thank you for all the hard work and enlightenment and counting++. My sign of respect/appreciation for one of the best in the business :)
    – Rohit416
    Nov 13 at 11:46















up vote
199
down vote



accepted










The error message is insufficiently informative, and that is my fault. Sorry about that.



The problem you are experiencing is a consequence of the fact that covariance only works on reference types.



You're probably saying "but IA is a reference type" right now. Yes, it is. But you didn't say that T is equal to IA. You said that T is a type which implements IA, and a value type can implement an interface. Therefore we do not know whether covariance will work, and we disallow it.



If you want covariance to work you have to tell the compiler that the type parameter is a reference type with the class constraint as well as the IA interface constraint.



The error message really should say that the conversion is not possible because covariance requires a guarantee of reference-type-ness, since that is the fundamental problem.






share|improve this answer



















  • 3




    Why you said it's your fault?
    – user4951
    Nov 7 at 15:04






  • 68




    @user4951: Because I implemented all of the conversion checking logic including the error messages.
    – Eric Lippert
    Nov 7 at 15:06






  • 1




    @PeterA.Schneider: I appreciate that. But one of my primary goals for designing the error reporting logic in Roslyn was in particular to capture not just what rule was violated, but furthermore, to identify the "root cause" where possible. For example, what should the error message be for customers.Select(c=>c.FristName) ? The C# specification is very clear that this is an overload resolution error: the set of applicable methods named Select that can take that lambda is empty. But the root cause is that FirstName has a typo.
    – Eric Lippert
    Nov 8 at 19:39






  • 2




    @PeterA.Schneider: I did a lot of work to ensure that scenarios involving generic type inference and lambdas used the appropriate heuristics to deduce what error message was likely to best help the developer. But I did a far less good job on the conversion error messages, particularly where variance was concerned. I've always regretted that.
    – Eric Lippert
    Nov 8 at 19:40






  • 3




    @EricLippert This is probably not the place to say this but that is why I (and many more like me) always look up to you Eric (always loved the way you explain), thank you for all the hard work and enlightenment and counting++. My sign of respect/appreciation for one of the best in the business :)
    – Rohit416
    Nov 13 at 11:46













up vote
199
down vote



accepted







up vote
199
down vote



accepted






The error message is insufficiently informative, and that is my fault. Sorry about that.



The problem you are experiencing is a consequence of the fact that covariance only works on reference types.



You're probably saying "but IA is a reference type" right now. Yes, it is. But you didn't say that T is equal to IA. You said that T is a type which implements IA, and a value type can implement an interface. Therefore we do not know whether covariance will work, and we disallow it.



If you want covariance to work you have to tell the compiler that the type parameter is a reference type with the class constraint as well as the IA interface constraint.



The error message really should say that the conversion is not possible because covariance requires a guarantee of reference-type-ness, since that is the fundamental problem.






share|improve this answer














The error message is insufficiently informative, and that is my fault. Sorry about that.



The problem you are experiencing is a consequence of the fact that covariance only works on reference types.



You're probably saying "but IA is a reference type" right now. Yes, it is. But you didn't say that T is equal to IA. You said that T is a type which implements IA, and a value type can implement an interface. Therefore we do not know whether covariance will work, and we disallow it.



If you want covariance to work you have to tell the compiler that the type parameter is a reference type with the class constraint as well as the IA interface constraint.



The error message really should say that the conversion is not possible because covariance requires a guarantee of reference-type-ness, since that is the fundamental problem.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 12 at 21:45

























answered Nov 7 at 8:41









Eric Lippert

528k14510401921




528k14510401921








  • 3




    Why you said it's your fault?
    – user4951
    Nov 7 at 15:04






  • 68




    @user4951: Because I implemented all of the conversion checking logic including the error messages.
    – Eric Lippert
    Nov 7 at 15:06






  • 1




    @PeterA.Schneider: I appreciate that. But one of my primary goals for designing the error reporting logic in Roslyn was in particular to capture not just what rule was violated, but furthermore, to identify the "root cause" where possible. For example, what should the error message be for customers.Select(c=>c.FristName) ? The C# specification is very clear that this is an overload resolution error: the set of applicable methods named Select that can take that lambda is empty. But the root cause is that FirstName has a typo.
    – Eric Lippert
    Nov 8 at 19:39






  • 2




    @PeterA.Schneider: I did a lot of work to ensure that scenarios involving generic type inference and lambdas used the appropriate heuristics to deduce what error message was likely to best help the developer. But I did a far less good job on the conversion error messages, particularly where variance was concerned. I've always regretted that.
    – Eric Lippert
    Nov 8 at 19:40






  • 3




    @EricLippert This is probably not the place to say this but that is why I (and many more like me) always look up to you Eric (always loved the way you explain), thank you for all the hard work and enlightenment and counting++. My sign of respect/appreciation for one of the best in the business :)
    – Rohit416
    Nov 13 at 11:46














  • 3




    Why you said it's your fault?
    – user4951
    Nov 7 at 15:04






  • 68




    @user4951: Because I implemented all of the conversion checking logic including the error messages.
    – Eric Lippert
    Nov 7 at 15:06






  • 1




    @PeterA.Schneider: I appreciate that. But one of my primary goals for designing the error reporting logic in Roslyn was in particular to capture not just what rule was violated, but furthermore, to identify the "root cause" where possible. For example, what should the error message be for customers.Select(c=>c.FristName) ? The C# specification is very clear that this is an overload resolution error: the set of applicable methods named Select that can take that lambda is empty. But the root cause is that FirstName has a typo.
    – Eric Lippert
    Nov 8 at 19:39






  • 2




    @PeterA.Schneider: I did a lot of work to ensure that scenarios involving generic type inference and lambdas used the appropriate heuristics to deduce what error message was likely to best help the developer. But I did a far less good job on the conversion error messages, particularly where variance was concerned. I've always regretted that.
    – Eric Lippert
    Nov 8 at 19:40






  • 3




    @EricLippert This is probably not the place to say this but that is why I (and many more like me) always look up to you Eric (always loved the way you explain), thank you for all the hard work and enlightenment and counting++. My sign of respect/appreciation for one of the best in the business :)
    – Rohit416
    Nov 13 at 11:46








3




3




Why you said it's your fault?
– user4951
Nov 7 at 15:04




Why you said it's your fault?
– user4951
Nov 7 at 15:04




68




68




@user4951: Because I implemented all of the conversion checking logic including the error messages.
– Eric Lippert
Nov 7 at 15:06




@user4951: Because I implemented all of the conversion checking logic including the error messages.
– Eric Lippert
Nov 7 at 15:06




1




1




@PeterA.Schneider: I appreciate that. But one of my primary goals for designing the error reporting logic in Roslyn was in particular to capture not just what rule was violated, but furthermore, to identify the "root cause" where possible. For example, what should the error message be for customers.Select(c=>c.FristName) ? The C# specification is very clear that this is an overload resolution error: the set of applicable methods named Select that can take that lambda is empty. But the root cause is that FirstName has a typo.
– Eric Lippert
Nov 8 at 19:39




@PeterA.Schneider: I appreciate that. But one of my primary goals for designing the error reporting logic in Roslyn was in particular to capture not just what rule was violated, but furthermore, to identify the "root cause" where possible. For example, what should the error message be for customers.Select(c=>c.FristName) ? The C# specification is very clear that this is an overload resolution error: the set of applicable methods named Select that can take that lambda is empty. But the root cause is that FirstName has a typo.
– Eric Lippert
Nov 8 at 19:39




2




2




@PeterA.Schneider: I did a lot of work to ensure that scenarios involving generic type inference and lambdas used the appropriate heuristics to deduce what error message was likely to best help the developer. But I did a far less good job on the conversion error messages, particularly where variance was concerned. I've always regretted that.
– Eric Lippert
Nov 8 at 19:40




@PeterA.Schneider: I did a lot of work to ensure that scenarios involving generic type inference and lambdas used the appropriate heuristics to deduce what error message was likely to best help the developer. But I did a far less good job on the conversion error messages, particularly where variance was concerned. I've always regretted that.
– Eric Lippert
Nov 8 at 19:40




3




3




@EricLippert This is probably not the place to say this but that is why I (and many more like me) always look up to you Eric (always loved the way you explain), thank you for all the hard work and enlightenment and counting++. My sign of respect/appreciation for one of the best in the business :)
– Rohit416
Nov 13 at 11:46




@EricLippert This is probably not the place to say this but that is why I (and many more like me) always look up to you Eric (always loved the way you explain), thank you for all the hard work and enlightenment and counting++. My sign of respect/appreciation for one of the best in the business :)
– Rohit416
Nov 13 at 11:46












up vote
24
down vote













I just wanted to complement Eric's excellent insider answer with a code example for those that may not be that familiar with generic constraints.



Change Something's signature like this: The class constraint has to come first.



public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA





share|improve this answer



















  • 2




    I'm curious... what exactly is the reason behind the significance of the ordering?
    – Tom Wright
    Nov 8 at 2:11






  • 5




    @TomWright - the spec doesn't, of course, include the answer to many "Why?" questions, but in this case does make it clear that there are three distinct types of constraints, and when all three are used they have to be specifically primary_constraint ',' secondary_constraints ',' constructor_constraint
    – Damien_The_Unbeliever
    Nov 8 at 7:05






  • 1




    @TomWright: Damien is correct; there is no particular reason I'm aware of other than the convenience of the author of the parser. If I had my druthers the syntax for type constraints would be considerably more verbose. class is bad because it means "reference type", not "class". I would have been happier with something verbose like where T is not struct
    – Eric Lippert
    Nov 10 at 0:49















up vote
24
down vote













I just wanted to complement Eric's excellent insider answer with a code example for those that may not be that familiar with generic constraints.



Change Something's signature like this: The class constraint has to come first.



public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA





share|improve this answer



















  • 2




    I'm curious... what exactly is the reason behind the significance of the ordering?
    – Tom Wright
    Nov 8 at 2:11






  • 5




    @TomWright - the spec doesn't, of course, include the answer to many "Why?" questions, but in this case does make it clear that there are three distinct types of constraints, and when all three are used they have to be specifically primary_constraint ',' secondary_constraints ',' constructor_constraint
    – Damien_The_Unbeliever
    Nov 8 at 7:05






  • 1




    @TomWright: Damien is correct; there is no particular reason I'm aware of other than the convenience of the author of the parser. If I had my druthers the syntax for type constraints would be considerably more verbose. class is bad because it means "reference type", not "class". I would have been happier with something verbose like where T is not struct
    – Eric Lippert
    Nov 10 at 0:49













up vote
24
down vote










up vote
24
down vote









I just wanted to complement Eric's excellent insider answer with a code example for those that may not be that familiar with generic constraints.



Change Something's signature like this: The class constraint has to come first.



public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA





share|improve this answer














I just wanted to complement Eric's excellent insider answer with a code example for those that may not be that familiar with generic constraints.



Change Something's signature like this: The class constraint has to come first.



public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 7 at 19:58









Peter Mortensen

13.3k1983111




13.3k1983111










answered Nov 7 at 16:14









Marcell Tóth

825115




825115








  • 2




    I'm curious... what exactly is the reason behind the significance of the ordering?
    – Tom Wright
    Nov 8 at 2:11






  • 5




    @TomWright - the spec doesn't, of course, include the answer to many "Why?" questions, but in this case does make it clear that there are three distinct types of constraints, and when all three are used they have to be specifically primary_constraint ',' secondary_constraints ',' constructor_constraint
    – Damien_The_Unbeliever
    Nov 8 at 7:05






  • 1




    @TomWright: Damien is correct; there is no particular reason I'm aware of other than the convenience of the author of the parser. If I had my druthers the syntax for type constraints would be considerably more verbose. class is bad because it means "reference type", not "class". I would have been happier with something verbose like where T is not struct
    – Eric Lippert
    Nov 10 at 0:49














  • 2




    I'm curious... what exactly is the reason behind the significance of the ordering?
    – Tom Wright
    Nov 8 at 2:11






  • 5




    @TomWright - the spec doesn't, of course, include the answer to many "Why?" questions, but in this case does make it clear that there are three distinct types of constraints, and when all three are used they have to be specifically primary_constraint ',' secondary_constraints ',' constructor_constraint
    – Damien_The_Unbeliever
    Nov 8 at 7:05






  • 1




    @TomWright: Damien is correct; there is no particular reason I'm aware of other than the convenience of the author of the parser. If I had my druthers the syntax for type constraints would be considerably more verbose. class is bad because it means "reference type", not "class". I would have been happier with something verbose like where T is not struct
    – Eric Lippert
    Nov 10 at 0:49








2




2




I'm curious... what exactly is the reason behind the significance of the ordering?
– Tom Wright
Nov 8 at 2:11




I'm curious... what exactly is the reason behind the significance of the ordering?
– Tom Wright
Nov 8 at 2:11




5




5




@TomWright - the spec doesn't, of course, include the answer to many "Why?" questions, but in this case does make it clear that there are three distinct types of constraints, and when all three are used they have to be specifically primary_constraint ',' secondary_constraints ',' constructor_constraint
– Damien_The_Unbeliever
Nov 8 at 7:05




@TomWright - the spec doesn't, of course, include the answer to many "Why?" questions, but in this case does make it clear that there are three distinct types of constraints, and when all three are used they have to be specifically primary_constraint ',' secondary_constraints ',' constructor_constraint
– Damien_The_Unbeliever
Nov 8 at 7:05




1




1




@TomWright: Damien is correct; there is no particular reason I'm aware of other than the convenience of the author of the parser. If I had my druthers the syntax for type constraints would be considerably more verbose. class is bad because it means "reference type", not "class". I would have been happier with something verbose like where T is not struct
– Eric Lippert
Nov 10 at 0:49




@TomWright: Damien is correct; there is no particular reason I'm aware of other than the convenience of the author of the parser. If I had my druthers the syntax for type constraints would be considerably more verbose. class is bad because it means "reference type", not "class". I would have been happier with something verbose like where T is not struct
– Eric Lippert
Nov 10 at 0:49


















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53185806%2fproblem-understanding-covariance-contravariance-with-generics-in-c-sharp%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







這個網誌中的熱門文章

Tangent Lines Diagram Along Smooth Curve

Yusuf al-Mu'taman ibn Hud

Zucchini