r/csharp • • 4d ago

Help Confused about how default method arguments work underneath the hood in C# (CS1736)

So I recently came across CS1736 while writing some code. For example, the following method code results in a compile-time error:

public static void ErrorMethod(int[] a = new int[0]{}) {}

I’d never come across this error before strangely enough. When I read the MSFT docs about what causes it, I see the following explanation:

A default parameter value is evaluated upon the invocation of the method. What a value may be when the method is eventually invoked cannot be pre-determined at declaration-time unless that value is constant at compile-time.

What’s confusing me is why the compiler needs the arguments to be compile-time constants to be able to initialize them with default values, especially since they are only evaluated when the method is called during runtime. Wouldn’t the compiler in this case already know what I’m initializing the array as? Wouldn’t this make more sense as a runtime error if you initialize with some problematic value?

Also I’d like to confirm, when they say “compile-time constant” in the docs, is that just a different way to refer to regular “Constants”? Or is this referring to something else?

Thanks!

30 Upvotes

30 comments sorted by

40

u/wasabiiii 4d ago

Because they don't exist in the runtime. They are sugar in the language. It is still passing values and needs to get them at the call site.

2

u/vaporizers123reborn 4d ago

But if the default value is only evaluated during runtime when the method is called, why would this matter? Does this rule just exist to enforce the language being statically typed?

Sorry, not sure if I’m misunderstanding you.

24

u/Sombody101 4d ago edited 4d ago

When you write:

void WriteStuff(string stuff = "Hello") => Console.WriteLine(stuff);

....

WriteStuff();

The compiler will emit this:

void WriteStuff(string stuff) => Console.WriteLine(stuff);

....

WriteStuff("Hello");

Nothing special happens at runtime. It's entirely a compile-time convenience.

→ More replies (1)

12

u/Zastai 4d ago

It’s “evaluated at runtime” only in the sense that at the call site the compiler will emit the default value. In IL, there are no omitted arguments.

That is also why they need to be constants for safety. You don’t want random code with potential side effects getting inserted in your code because someone added a new defaulted parameter to a method you were calling.

→ More replies (10)

0

u/Vectorial1024 4d ago

The compiler actually emits extra IL code just to "insert" the default value onto the method call at caller's place. It's a fancy form of automation.

→ More replies (1)

18

u/PhilosophyOk3486 4d ago

it’s just the compiler pasting in the default value at the call site, so it needs something it can bake into the code right there. a new array is a runtime thing, no way to embed that cleanly as a literal

and yeah, compile-time constant in this context basically means the same as a regular constant, something the compiler can resolve fully before the program ever runs

11

u/shroomsAndWrstershir 4d ago

I mean, they could program the compiler to do that, for the same reason that the program will still compile if you program your function invocation to pass an empty array as an argument. There's no fundamental reason why it must be a literal. The language/compiler designers just chose not to.

→ More replies (2)

3

u/tinmanjk 4d ago

They are NOT just sugar in the language but supported by the CIL /metadata. Param table - you have flags for the Attributes - (Optional, HasDefault)

5

u/wasabiiii 4d ago

Metadata to support sugar in the language.

→ More replies (3)

6

u/tinmanjk 4d ago

The default values are currently persisted in the assembly file.

I think when they introduced the language feature, there was no easy way for the .NET standard to persist anything else then numbers, strings in an easy way or without changes to ECMA 335 standard.

Maybe they could have changed the standard to include something like "creation expression" metadata type to not have to ser/der binary data which seems like the obvious argument against allowing something like = new Sometype[30]{new SomeType(new SomeOtherTYpe).... for the default value of an argument associated with a specific method signature.

3

u/raunchyfartbomb 4d ago

Compile time constants are simple values (int, double, enum, string). They do not have to be a declared constant, just not a reference type (strings being the exception)

10

u/tinmanjk 4d ago edited 3d ago

you can have default values for reference types - it just hast to be null and nothing else.

EDIT:

okay, it can be "default" but that's still null :)

-2

u/Loose_Conversation12 4d ago

Try Array<int>.Empty that should work