Version Used: any version since dynamic
was introduced in .NET 4.0 (tested with VS 2012 & 2015).
Steps to Reproduce:
dynamic
variable.Sample code:
private static double Test1()
{
dynamic stringVal = "";
//Working as expected.
//The type of the dynamic variable shouldn't be checked.
return stringVal;
}
private static double Test2()
{
dynamic stringVal = "";
//Not working as expected.
//This code shouldn't compile because it includes an unfixable error.
return
(
true ? stringVal : "string2"
);
}
Expected Behavior: the ternary operator should account for the most restrictive type (string
in the Test2()
case) and, consequently, this code shouldn't compile (i.e., either because stringVal
type isn't string
, against what the ternary operator expects; or because the ternary operator returns a string
type, against what the return
statement expects). The fact of a priori not knowing the exact reason for the error isn't relevant here as far as it will certainly occur (what the compiler could easily understand in case of relying on the aforementioned giving-preference-to-the-most-restrictive-alternative approach).
Actual Behavior: dynamic
variables are being taken as absolute references in ternary operators (perhaps in other situations too) what avoids the compiler to see any problem in these cases. That is, a code with a ternary operator including a dynamic
variable will always compile, independently upon any internal/external type incompatibility. Such a reality shouldn't occur on account of the dynamic
scope (i.e., only affecting the behaviour of the variables declared as dynamic
).
Fairly sure this is by design. Ternary expressions can only have a single type, and since you used a dynamic
as one of the operands that renders the type of the entire expression as dynamic
. The "string2"
is then implicitly cast to dynamic
within that expression and the binder is attempting to convert that to double
. Effectively the compiler is doing this:
dynamic stringVal = "";
dynamic $temp;
if (condition) {
$temp = stringVal;
}
else {
$temp = "string2";
}
double $retval = $temp;
return $retval;
@HaloFour Yes, I know that this is by design. That's why I consider it a design flaw.
As explained, the most logical approach is giving preference to the most restrictive alternative (string
in this case). If we have a string
and a can-be-anything, there are two possible scenarios:
string
; a DateTime
, for example. We have string
-DateTime
.string
. We have string
-string
.Scenario 1 would crash because a ternary operator cannot deal with two different types, but the compiler wouldn't warn about it. This isn't a problem because of being precisely the whole point of dynamic
(not knowing the type at compilation).
On the other hand, the output of such a ternary operator is certain without having to check the dynamic
variable type: either a string
or an error (which happens at compilation; thus, from the compiler point of view, there is only one option: string
). As far as the proposed function expects a double
, this code shouldn't compile.
Even you can see the problem from a different perspective. By replacing dynamic
with object
, you have the following non-compilable code:
private static double Test22()
{
object stringVal2 = "";
//Working as expected. It doesn't compile.
return
(
true ? stringVal2 : "string2"
);
}
Logically, object
and dynamic
are different. But the question is why dynamic
can affect more than just the dynamic
variable itself? I accept that the type of stringVal2
is known and the one of stringVal
is not (also accept that, in case of assigning a wrong type to stringVal
, I would get a runtime error). But why stringVal
is also affecting how the compiler deals with the ternary operator? Why you can have two variables with exactly the same value & type, one of them being compilable and the other one not? And even more important: why not relying on the really easy and pretty logical proceeding of giving preference to the most restrictive option (i.e., string
in this case) and avoiding any problem?
I want also to highlight that firstly wrote this issue in CoreFX (https://github.com/dotnet/corefx/issues/11825), where I included more ideas which some people might want to look at. Here I made it briefer to meet the issue-template requirements.
Scenario 1 would crash because a ternary operator cannot deal with two different types
Are you talking about something like this?
``` c#
private static double Test()
{
dynamic doubleVal = 3.14;
return true ? doubleVal : "string";
}
Because that does not crash. The ternary operator cannot deal with two incompatible types, but that's not the case here. Here, you have `dynamic` and something implicitly convertible to `dynamic`, so the ternary operator works fine.
> On the other hand, the output of such a ternary operator is certain without having to check the `dynamic` variable type: either a `string` or an error.
I don't think it is certain. Even if there is no implicit conversion at compile time, there could be one at runtime. Either because the it's actually a different type (e.g. with `(Base)new Derived()`, the compiler sees that the compile-time type is `Base`, but at runtime it will be `Derived`, if `Derived` has implicit conversion and `Base` does, this would make a difference), or because the type changed (e.g. `string` added an implicit conversion to `double` and you upgraded to the new version of `string`, but did not recompile).
> By replacing `dynamic` with `object`, you have the following non-compilable code
That code fails with:
> CS0266 Cannot implicitly convert type 'object' to 'double'. An explicit conversion exists (are you missing a cast?)
That error explains exactly why the `dynamic` code compiles: there is an implicit conversion from `dynamic` to `double`. So, just like this code with explicit conversion from `object` compiles (and then throws at runtime):
``` c#
private static double Test()
{
object stringVal2 = "";
return (double)(true ? stringVal2 : "string2");
}
so should the dynamic
code with implicit conversion from dynamic
compile.
In short: the whole point of dynamic
is to make type-related decisions at runtime, instead of compile time. Trying to make more decisions at compile-time for it does not make much sense and has to be considered very carefully.
@svick Thanks for the correction, I did chose a bad example (updated my post). But here you have an even more curious issue:
private static double Test3()
{
double doubleVal2 = 0.0;
//Doesn't compile.
return true ? doubleVal2 : "string";
}
Your code (with a dynamic
variable) runs fine, but this one doesn't even compile (the kind of behaviour which seems logical to me when dealing with the ternary operator)?! How can be this the case? A variable which can be any type works fine when being a specific case (double
); but a variable actually having such a type doesn't even compile?!
This is starting to become a bit too weird. And all this might have been avoided with the aforementioned suggestion of preferring the most restricted alternative! (As almost every time when dealing with complex realities, keeping it simple is the solution :)).
@varocarbas The ternary operator chooses one of the two types, as long as there is an implicit conversion from the other type. So:
double
and dynamic
, it chooses dynamic
, because there is an implicit conversion from double
to dynamic
double
and object
, it chooses object
, because there is an implicit conversion from double
to object
double
and string
, it fails to compile, because there is no implicit conversion between double
and string
And all this might have been avoided with the aforementioned suggestion of preferring the most restricted alternative!
Consider this code:
``` c#
private static dynamic Test()
{
dynamic doubleVal = 0.0;
return true ? dynamicVal : "string";
}
```
Are you saying that the type of the ternary expression here should be string
? And so this code should fail at runtime? That's not what I would expect and I also don't see how would that solve anything. Or did I misunderstood what you meant?
@svick You did understand perfectly what I meant. But I think that we have different ideas on this front. Let me do a quick overview to synchronise.
My understanding of how types work (and my expectations on this front) is based upon the native ones (in its widest sense, plainly as opposed to dynamic
, object
or similar). I expect any other "supra-type", like object
or dynamic,
to basically follow the same rules. That is, if I see that (true ? doubleVar : stringVar)
cannot compile (logical on account of the fact that two different types cannot be compared between each other); then, I expect the following:
dynamic dynamicDoubleVar = doubleVar;
(true ? dynamicDoubleVar : stringVar); // Shouldn't compile.
object objectDoubleVar = doubleVar;
(true ? objectDoubleVar : stringVar); // Shouldn't compile.
This is what I call consistency (intuitiveness, the most logical thing, etc.). I expect differences in the object
and dynamic
variables, but only in case of affecting to themselves (e.g., conversion from/to native types). After the conversion to the given native type has been performed, I expect them to behave identically (or, at least, similarly enough) to what the given native type would do. Quite a few of the samples in this thread show that dynamic
variables don't follow these rules and do behave differently than the corresponding native type (even with "external effects", by redefining how to analyse a ternary operator/determine what compilable is).
With "more restrictive alternative" I meant applying the (kind of) general rule of always chosing the narrowest applicability. In the ternary operator, if you can choose between anything and string
, choose string
(because you have to choose something; a type has to be returned); assume that the dynamic
variable is a string
and perform all the subsequent actions accordingly. I proposed this by applying the aforementioned ideas of extending what works for native types. If you want to create a new categorisation as the one that you are including in your last post (where dynamic
/object
are considered independent entities, rather than representations of the corresponding native types; bear in mind that, for me, dynamic
converted to double
should behave like double
), my suggestion wouldn't hold.
I do expect any ternary operator formed by two different types (e.g., double
-string
or dynamic_double
-string
) to not compile. I also expect a ternary operator to return the type in which the dynamic
variable has been converted (not just dynamic
, which has no meaning and a huge scope). I expect to use dynamic
(or, eventually, object
) as a perfectly-controlled way to avoid expressly telling the type; but this variable should also behave identically (almost; logically, all the direct type checks will be ignored) as any other variable of that type. I also expect a consistent framework where different alternatives always output identical results. That's why I consider this implementation to be faulty.
dynamic
isn't a "supra-type", it's your explicit declaration to the compiler that it should disable type checking and to "figure it out" at runtime.
I expect to use
dynamic
as a perfectly-controlled way to avoid expressly telling the type
This might be where your confusion is. dynamic
is not "I'm not going to tell you the type, figure it out", that's what var
is for (and there are good reasons why it's so limited).
Instead dynamic
is "at compile time, assume that this value could be any type and figure out what to do at runtime".
@HaloFour Nothing is a "supra-type", this is a word I made up to explain my ideas. I meant explicitly declaring a given type (double
, string
or whatever) vs. doing it indirectly. The whole point of my posts was highlighting that a dynamic
variable assigned to a double
one should behave identically than a variable originally defined as double
(except, logically, for the dynamic
peculiarities of no type check).
Or, in other words, you shouldn't be able to reach certain scenario (e.g., a ternary operator being compilable) by using one alternative and not being able to do so by using a different approach which, on the paper, is identical (variable with same type and value).
@svick I am perfectly aware about the differences between var
and dynamic
. Sorry if I have provoked some misunderstanding while trying to make my explanations more clear.
I am not confused at all. I know perfectly what dynamic
(or var
or object
or any other thing) can deliver. I do understand what seems to be the logic behind the current implementation. I plainly don't agree with it, because of considering this behaviour inconsistent with the remaining parts (+ even with the a priori most logical output: why affecting anything else than just the variable defined as dynamic
?). For me, you (and @HaloFour) are the ones being confused here, because you aren't trying to understand my point (not even my intention); you just want to convince me about how things have to be (by also trying to justify the validity of anything); not agreeing with you is interpreted as "you don't understand" (what can I say about that?). No, I am afraid that you are the ones not understanding here (neither interested in doing so).
In any case, I will stop over-explaining a point which, in my opinion, was crystal clear since my first post (reading the original CoreFX post might also be helpful). I don't want to convince you or anyone else about anything; neither to start a faith war about what is, should be, will be (quite bad past experiences on this front). The whole point of my post was highlighting an issue which is quite evident to me (I was completely focused on a pretty complex development; by using dynamic
in a relevant way for almost the first time; and I only needed to see this behaviour once to know that something wasn't right). I shared my impressions by assuming that they might be useful to more people (= others thinking like me); by expecting an objective-correction-prone discussion, rather than a blind repetition of how things have to be (and people thinking otherwise being wrong or bad or enemies or trolls or whatever; sorry, but as said, I had quite bad past experiences with this community). If the .NET community/team thinks different than me, I would accept it and move on.
I understand your use-case, but believe that current behavior is very reasonable.
It really comes down to 2 principles. You can probably find them phrased better in some older Eric Lippert's posts, but roughly:
dynamic
statically behaves like type that has every method and property and is implicitly convertible to and from every type.Like so many C# features, both strike fine balance in simplicity, power and safety (my opinion). Adding special casing for their interaction would make a lot of harm. For the price of some safety, it would remove expresivity and add a lot of complexity to the language.
@zippec It is a bit difficult to argue against an E. Lippert quote (good argumentation technique, by the way ;)). In any case and without daring to think that my knowledge is better than his, the "one candidate that all others can implicitly convert to chose that one, otherwise its an error" part doesn't sound too good to me. In fact, it doesn't even seem applicable to this ternary operator case (other than for dynamic
). For example, it doesn't apply to object
(which does show the behaviour which I expect).
Object
variables (or var
or any other not-expressly-typed variable), after being converted to another type, behave like this other type. Bear in mind that all these variables are meant to be converted to something; they have to be assigned before being used in the ternary operator (dynamic
variables too). So the question is: why the dynamic
variable wants to remain dynamic
(= unassigned) even after an assignation did occur? And why no other variable can do such a thing?
What is much, much more important: how can be a so irregular behaviour be supported after seeing the inconsistent consequences (as shown in various samples in this thread)? If you have a ternary operator including a string
, it has to output a string
or an error (no need to check the other member). This is true in each single scenario, except when dynamic
is present (?!). Because apparently and unlikely in any other case, the ternary operator assumes that the type is dynamic (?!), rather than string
.
The reason why I created this issue was that, while writing a code including dynamic
variables, I did a mistake similar to the one described in my first sample; a mistake which I could never do (VS not letting me compile) in any other situation. Such a scenario is so different to what C# usually delivers that I wouldn't have ever expected it to happen. Imagine that I didn't see the bug and did continue writing code. When debugging it at a later stage, I would never have looked at something like this: code compiling despite having a faulty ternary operator?! Should I stop trusting Visual Studio recognition capabilities? And/or be afraid of each new extension because of perhaps provoking a redefinition of the rules which have always been applicable to anything else?
A ternary operator (and perhaps other things; as said, this behaviour might also be occurring in other scenarios) behaves always in the same way (e.g., if it includes a string
and the expected value is a double
, it shouldn't compile). You cannot bring a special element making it behave differently. This is the point which I am trying to make and which some of the last comments seem to be losing: it is not about the dynamic
variable itself, it is about how it affects an external element (i.e., the ternary operator). How can it do that and why it is the only element which can do such a thing? My opinion: a mistake. The solution: make sure that the ternary operator (or anything else) behaves as usual by adapting dynamic
to it, not the other way around.
As explained, the most logical approach is giving preference to the most restrictive alternative
That is the opposite of the most logical approach. The ternary operator gives preference to the _least_ restrictive alternative. For example, if the values are of types object
and string
, the result is of type object
, not string
. The type dynamic
is not a special case here.
If you have a ternary operator including a
string
, it has to output astring
or an error (no need to check the other member). This is true in each single scenario, except whendynamic
is present (?!).
It's also true for object
:
``` c#
object o = 42;
var x = condition ? o : "foo"; // the type of x is object, the value of x is either 42 or "foo"
And it's also true for types that have implicit conversion from `string`, like `XNamespace`:
``` c#
XNamespace ns = "ns";
var x = condition ? ns : "s"; // the type of x is XNamespace, the value of x is XNamespace with NamespaceName of "ns" or "s"
The reason why I created this issue was that, while writing a code including
dynamic
variables, I did a mistake similar to the one described in my first sample; a mistake which I could never do (VS not letting me compile) in any other situation. Such a scenario is so different to what C# usually delivers that I wouldn't have ever expected it to happen. Imagine that I didn't see the bug and did continue writing code. When debugging it at a later stage, I would never have looked at something like this: code compiling despite having a faulty ternary operator?! Should I stop trusting Visual Studio recognition capabilities? And/or be afraid of each new extension because of perhaps provoking a redefinition of the rules which have always been applicable to anything else?
The whole point of dynamic
is to disable static type checking. If you want to make sure VS finds most of your type errors, _don't use dynamic
_.
The solution: make sure that the ternary operator (or anything else) behaves as usual by adapting
dynamic
to it, not the other way around.
It already does. If you look at the specification for the ternary operator, you won't see any special rules for dynamic
.
@gafter Perhaps I didn't phrased my point properly. The idea I was trying to transmit was that if you have one-type vs. any-type, you should choose one-type (because choosing any-type would make the condition always true). I meant when the ternary operator is determining whether the same-type condition holds (and consequently the code is compilable) or not.
@svick As written above to gafter, perhaps I didn't phrased my point properly. I don't have any problem with the returned type, but with what the ternary operator does while comparing types (and a dynamic
variable is involved). The problem seems to be that the compiler skips any type check involving a dynamic
variable by ignoring that, in some cases (like in the ternary operator), knowing the type of the surrounding variables is enough.
If I have a compilable ternary operator (even though it might crash at runtime because of the dynamic
peculiarities), I wouldn't see any problem in returning a dynamic
variable (logically, assigned to whatever type is applicable). The problem I am highlighting is that the fact of having a dynamic
variable converts a non-compilable ternary operator into compilable.
For example: (true ? dynamicVar : stringVar)
is fine when expecting a string
(and it might return a dynamic
variable). The problem is that this ternary operator also compiles in case of expecting a double
or a DateTime
or whatever. That is: just the fact of having a dynamic
variable has changed the way in which the compiler treats the ternary operator?! It doesn't even analyse stringVar
and confirms that no compilation is possible.
Regarding your
The whole point of dynamic is to disable static type checking. If you want to make sure VS finds most of your type errors, don't use dynamic.
You are again not getting the point: my problem isn't the behaviour of the dynamic
variable per se. I accept the consequences and the errors which choosing the wrong type might provoke. The problem is that this variable makes the ternary operator (and perhaps other situations too) to behave differently than in any other scenario. In fact, the mistake of the referred code wasn't the dynamic
variable, but the string
one!! VS would have complained in any other case, but when I put a dynamic
variable there it plainly doesn't say anything.
@gafter Re-clarifying my point. I meant comparing the expected type with the one returned by the ternary operator (assumed to be the most restricted one, string
in my example). That is, if you have a ternary operator with one string
, you should check the expected type vs. this string
(because the other element has also to be a string
). After confirming that everything is OK (expected type matches what the ternary operator can deliver), you might then make further considerations (what you and @svick were referring about preferring dynamic
, then object
, etc.). In fact, this kind of check is already being performed when dealing with object
which, as shown, doesn't allow a code on these lines to compile.
That's why I prefer to include descriptive enough examples, rather than likely to be over-complicated/confusing clarifications (+ small details being misinterpreted, etc.). My first code shows this point very clearly: you have a ternary operator including a string
variable when a double
is expected. This situation is non-compilable independently upon what the other member is. Even by bringing the dynamic
essence to its pure limits, this situation shouldn't compile (the dynamic
variable being anything other than a string
should be an error/non-compilable).
I have realised that dynamic
variables also allow non-compilable methods to compile, as shown in the code below.
``` C#
private static void CallingMethod()
{
DateTime dateVar = ReturnString(""); //It doesn't compile.
dynamic dynamicVar = "";
dateVar = ReturnString(dynamicVar); //Now it compiles.
}
private static string ReturnString(string arg)
{
return arg;
}
```
I found the first sample already quite descriptive, but this one seems even clearer.
@varocarbas Reproduced. http://csharppad.com/gist/4555adee9cbd59949020fb91be849279
That doesn't looks like it should have compiled.
@tenor
I see this behaviour in VS 2012 & 2015. To not mention that it is quite compatible with the first sample I included here (i.e., not too weird/non-expectable).
Apparently, this web-app you are using isn't too reliable (first time I use it), but I was able to make it work as per my description anyway. You didn't copy my whole code, but let the CallingMethod()
contents outside. Just copy all the code as it is, with two methods and you should be able to reproduce the described behaviour: an error with DateTime dateVar = ReturnString("");
and compiling fine when only dynamic dynamicVar = ""; DateTime dateVar = ReturnString(dynamicVar);
is present.
Apparently, this web-app you are using isn't too reliable (first time I use it),
:smile: Looks like MS needs to market C# Script mode a bit more. I wrote the app. Please drop me a line off-forum on what was confusing.
The issue I see here is that the the dynamic argument is contagious enough to prevent the compiler from checking if the return type of the ReturnString
method can be received by the dateVar
variable.
The code will _always_ fail at runtime so I think you're right. The compiler should detect it.
@tenor
I wrote the app
Ups! Sorry. I meant that, logically, it isn't like using Visual Studio. For example, it isn't clear what is in there when you start writing (I guess that a class inside namespace?) and the errors are logically not as descriptive. I didn't get a bad impression of it (it seems easy to use and quite quick); but I do certainly prefer VS. In any case, I guess that it can be useful for people without VS.
The compiler should detect it.
I think so. That's why I started this issue. I am currently writing a quite big code with lots of dynamic
variables.
From the spec section 7.5.4.
Compile-time checking of dynamic overload resolution
For most dynamically bound operations the set of possible candidates for resolution is unknown at compile-time. In certain cases, however the candidate set is known at compile-time:
• Static method calls with dynamic arguments
• Instance method calls where the receiver is not a dynamic expression
• Indexer calls where the receiver is not a dynamic expression
• Constructor calls with dynamic arguments
In these cases a limited compile-time check is performed for each candidate to see if any of them could possibly apply at run-time.This check consists of the following steps:
• Partial type inference: Any type argument that does not depend directly or indirectly on an argument of type dynamic is inferred using the rules of §7.5.2. The remaining type arguments are unknown.
• Partial applicability check: Applicability is checked according to §7.5.3.1, but ignoring parameters whose types are unknown.
If no candidate passes this test, a compile-time error occurs.
@varocarbas
If I'm interpreting this correctly, the spec says the code is fine because the proper overload is unknowable at compile time, however in some cases, the candidate overloads can be determined at compile time in certain scenarios. The code you supplied matches the first scenario. (static method call with dynamic arguments).
@tenor
If I'm interpreting this correctly,
No, you are certainly not. You are talking about a scenario which isn't even present in this sample (there is no various overloads to choose from, just one).
I don't see the point of continuing with this discussion. My example is very clear and the underlying ideas (i.e., dynamic
variable just passed as an argument provoking the method to behave differently than what would have happened by passing any other variable; and an impossible-to-be-run-and-consequently-not-compilable code to compile) should be clear to anyone.
Note that I am trying to be nice (promised it to the .NET team); and this is actually the only reason why I answered your first two messages as I did (otherwise, my first answer would have been completely different because of thinking that being nice would have plainly provoked everyone to waste their time). Feel free to continue discussing with others, but please involve me in the discussion only when sharing relevant points (ideally supported with descriptive examples, because abstract ideas are an easy way to lose track).
Hopefully, you will be not feel bothered by this somehow-clear-but-still-pretty-soft answer :)
@varocarbas I was trying to say that the spec backs your claim.
There could be potential overloads in a derived type in a different assembly that the compiler is unaware of. That's why the compiler allows the code.
However, the spec is saying that for calls to static methods (like in the example you provided), there will be no derived types and so the compiler should be able to catch the error.
@tenor
I have some problems to see the compatibility between your original "the spec says the code is fine" and "the spec backs your claim", because I understand that the code I posted was wrong (= the compiler thinks differently than what I expect). But this is precisely the problem with abstract, without-code-sample talking and why I don't want to continue this conversation (IMHO, a pointless waste of time).
There could be potential overloads
Logically, but you were using this text to refer to a scenario where only one overload was present. Do you want to consider multiple overloads? Excellent! Show a code or explain how the described behaviour works under such conditions. But using a given spec to justify/criticise a completely different situation doesn't seem too correct.
That's why the compiler allows the code
Again no. This is a 1-overload scenario, the multiple-overload rules don't apply here. Your spec is applied when determining which overload, out of various ones, should be used. Here there is no possible confusion; just one overload, no doubt about which one to use.
The following code does illustrate a multi-overload scenario:
``` C#
private static void CallingMethod()
{
//DateTime dateVar = ReturnString(""); //It doesn't compile.
dynamic dynamicVar = new DateTime(); // "";
DateTime dateVar = ReturnString(dynamicVar); //Now it compiles.
}
private static string ReturnString(string arg)
{
return arg;
}
private static string ReturnString(DateTime arg)
{
return arg.ToString();
}
```
If you test this code, you would see that there is no confusion here either: dynamic dynamicVar = new DateTime();
provokes ReturnString(DateTime arg)
to be selected; and dynamic dynamicVar = ""; ReturnString(string arg)
. There is an error because of trying to store a string
in a DateTime
variable, but the overload-selection part works fine. Again this code shouldn't compile (because both methods return a string
rather than the expected DateTime
variable) and again it does. So the number of overloads doesn't seem to be an issue here; but you are free to go deeper into all this by considering more complex situations.
calls to static methods (like in the example you provided), there will be no derived types and so the compiler should be able to catch the error
I am neither sure about what you are trying to say there nor I honestly care. See, I think that I have fully covered my being-nice promise, also that my position is quite clear. I don't want to continue with this conversion, please respect that.
A new example of the referred behaviour (calling non-static from static); this time with the peculiarity that it works on my Visual Studio 2012, but not on my 2015 version (?! Might it have to do with the configuration of the compiler? Note that I am using the default one in both cases).
``` C#
public static void CallingStaticMethod()
{
NonStaticMethodNormal("anything"); //It doesn't compile.
dynamic value = "anything";
NonStaticMethodDynamic(value); //It compiles in VS 2012 (wrong IMO), but not in VS 2015 (right IMO).
NonStaticMethodDynamic("anything"); //It doesn't compile.
}
public void NonStaticMethodNormal(string arg) { }
public void NonStaticMethodDynamic(dynamic arg) { }
```
As said, I am working on a somehow big code where I am using dynamic
variables a lot (BTW, quite happy with them so far, other than for these compiling-anything issues) and that's why I am finding so many descriptive situations. In any case, I guess that my point is already completely clear and am not planning to post more samples.
Most helpful comment
That is the opposite of the most logical approach. The ternary operator gives preference to the _least_ restrictive alternative. For example, if the values are of types
object
andstring
, the result is of typeobject
, notstring
. The typedynamic
is not a special case here.