Question about External References

T

Tim Roberts

I need a patient explanation.

As background, I've been programming for about 35 years. I consider myself
a C++ guru and a Python expert, and I'm just now ramping up on C#. There
is something that confuses me. Well, there are many things that confuse
me, but more on that later.

In C++, when one source file needs to refer to things compiled in another
source file, we have to use prototypes and declarations to let the compiler
know about it. In Python, if I need to refer to a class in another module,
I have to import the module. In virtually every language I've ever used, I
have to take some kind of explicit action to tell the compiler where to
find external modules.

In C#, there is apparently some kind of magic going on. As an example,
let's say I have code in one.cs that wants to instantiate an object of
class Two, something like:

Two myObject = new Two();
myObject.GoDoSomething();

OK, now let's say that class Two is defined in two.cs.

If I do "csc one.cs", I'll get an error, because Two is not known. But if
I say "csc one.cs two.cs", then everybody is happy. Why does that work?
There's nothing in one.cs that tells the compiler where to find "Two". The
compiler can't finish compiling "one.cs" until it sees "two.cs"; it won't
even know that Two has a method called GoDoSomething until it finishes with
"two.cs".

How does the compiler handle this? Does it just concatenate all of the
source modules into one big compilation unit, and scan for class
definitions before compiling the code?
 
A

Arne Vajhøj

Tim said:
As background, I've been programming for about 35 years. I consider myself
a C++ guru and a Python expert, and I'm just now ramping up on C#. There
is something that confuses me. Well, there are many things that confuse
me, but more on that later.

Java is much closer to C# than C++ and Python.
In C++, when one source file needs to refer to things compiled in another
source file, we have to use prototypes and declarations to let the compiler
know about it. In Python, if I need to refer to a class in another module,
I have to import the module. In virtually every language I've ever used, I
have to take some kind of explicit action to tell the compiler where to
find external modules.

In C#, there is apparently some kind of magic going on. As an example,
let's say I have code in one.cs that wants to instantiate an object of
class Two, something like:

Two myObject = new Two();
myObject.GoDoSomething();

OK, now let's say that class Two is defined in two.cs.

If I do "csc one.cs", I'll get an error, because Two is not known. But if
I say "csc one.cs two.cs", then everybody is happy. Why does that work?

There's nothing in one.cs that tells the compiler where to find "Two". The
compiler can't finish compiling "one.cs" until it sees "two.cs"; it won't
even know that Two has a method called GoDoSomething until it finishes with
"two.cs".

How does the compiler handle this? Does it just concatenate all of the
source modules into one big compilation unit, and scan for class
definitions before compiling the code?

The compiler compiles both files in a single compilation.

AFAIK it does not physical concatenate the files, but I guess
you could think of it that way (but note that the using directives
are local to the physical file).

You may find it much more logical to do:

csc /t:library two.cs

which build two.cs to two.dll

csc /r:two.dll one.cs

which builds one.cs to one.exe

!!

Arne
 
G

Göran Andersson

Tim said:
I need a patient explanation.

As background, I've been programming for about 35 years. I consider myself
a C++ guru and a Python expert, and I'm just now ramping up on C#. There
is something that confuses me. Well, there are many things that confuse
me, but more on that later.

In C++, when one source file needs to refer to things compiled in another
source file, we have to use prototypes and declarations to let the compiler
know about it. In Python, if I need to refer to a class in another module,
I have to import the module. In virtually every language I've ever used, I
have to take some kind of explicit action to tell the compiler where to
find external modules.

In C#, there is apparently some kind of magic going on. As an example,
let's say I have code in one.cs that wants to instantiate an object of
class Two, something like:

Two myObject = new Two();
myObject.GoDoSomething();

OK, now let's say that class Two is defined in two.cs.

If I do "csc one.cs", I'll get an error, because Two is not known. But if
I say "csc one.cs two.cs", then everybody is happy. Why does that work?
There's nothing in one.cs that tells the compiler where to find "Two". The
compiler can't finish compiling "one.cs" until it sees "two.cs"; it won't
even know that Two has a method called GoDoSomething until it finishes with
"two.cs".

How does the compiler handle this? Does it just concatenate all of the
source modules into one big compilation unit, and scan for class
definitions before compiling the code?

Well, that is pretty much what it does, except it doesn't scan before
compiling.

The files are compiled into the same assembly, and as long as the
classes are in the same namespace, they can use each other without
specifying where they are. It's namespaces that matter rather than in
which file a class is, as long as they are compiled at the same time.

As the compilation is done in stages, every reference doesn't have to be
specified for each file that is compiled. Typically a C++ compiler
compiles each file separately, and then uses a linker to resolve all the
references. The C# compiler works similarly, only you don't see the
separate stages as clearly.
 
T

Tim Roberts

Arne Vajhøj said:
The compiler compiles both files in a single compilation.

AFAIK it does not physical concatenate the files, but I guess
you could think of it that way (but note that the using directives
are local to the physical file).

You may find it much more logical to do:

csc /t:library two.cs

which build two.cs to two.dll

csc /r:two.dll one.cs

which builds one.cs to one.exe

Sure, that's the familiar model, and I've already explored the /t:module
option as well. However, that doesn't help me refine my mental model.

I've done compiler work; I know how tricky it is to handle forward
references. The implication is that the compiler makes multiple passes
through the sources: one, for example, to collect all the class names and
public methods, and another to compile the code, but that isn't exactly the
right model, either.

It may be that I'm searching for implementation details that aren't easily
available.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top