Archived

This topic is now archived and is closed to further replies.

patindahat

C# Cons?

Recommended Posts

I've been reading up on C# and found that it does many of the things I've been writing code for with the CLR. And it has a few other advantages to C++ (biggest one is Garbage collection which I'm I can't wait till MS Office, IE Explorer, Outlook and all those applications start using). But nothing that good comes free, I know it takes more processing power because everything is passed through another DLL thats constantly running the the background (CLR) but what else? Do you loose some of C++'s low level power? Do you loose the speed of the created binaries? What about fail safes, as in const keywords and the such, I've heard they got rid of many "useless" keywords but I personally love them as compiler errors are much better than run time. Pat - Ex nihilo nihilus [edited by - patindahat on August 10, 2003 4:35:24 PM]

Share this post


Link to post
Share on other sites
"Lose," not "loose." Pet peeve.

Anytime you run on an abstraction layer you lose some low-level power. Question is how much of it do you really use? At work we frequently encounter code that uses pointers and have to invent workarounds as we port things to C#.

I miss templates quite a bit in C#, thats why I continue to develop things in C++ for the moment.

They didn''t get rid of "useless" keywords (I can''t think of any keyword that''s useless at this second), they added more to complement the increased feature set. Most are pretty reasonable, my favorite has to be "unsafe." If only I could #define that to be "dangerous" or "risky."

There is a wealth of discussion on the net about C# performance tradeoffs, including one very long post detailing why the garbage collector works the way it is, and why there isn''t deterministic finalization. Its worth reading.

Share this post


Link to post
Share on other sites
Thank you for the opinion.... Thats excatly what I''m looking for. One last quick question before I make my decision on which version of visual studio to buy, can you write unmanaged C++ code in Visual C++ .NET ?

Pat - Ex nihilo nihilus

Share this post


Link to post
Share on other sites
quote:
Original post by flangazor
What does this thread have to do with Cons? There are no cons cells mentioned anywhere in this thread.


Do you feel like you were tricked into reading this thread because of the title?

Share this post


Link to post
Share on other sites
quote:
Original post by Ratman
quote:
Original post by flangazor
What does this thread have to do with Cons? There are no cons cells mentioned anywhere in this thread.


Do you feel like you were tricked into reading this thread because of the title?

I do. I want my money back.

Share this post


Link to post
Share on other sites
Closure <= Object
Reading this paper on lambda-dropping, I was reminded of something that nagged me while writing the Hotdog Scheme compiler. The other functional language compiler writers were using the same encoding for .NET''s OO bytecode language (CIL) as they did for C: lambda-lift all functions to reduce environments, and make direct calls to static methods with the environment, should there still be one, as the first argument. Rather than use this encoding, I implemented the other obvious encoding: a closure is an object with a single "apply" function, where private fields in the object are used to capture the environment (I used a private array). I quickly added a "case-lambda" form to generate a closure with multiple entry-points distinguished by arity. This made + and others faster because I didn''t handle varargs inefficiently in most cases.

The criticism of this approach is (1) object allocation is slow, (2) accessing the environment variables is slow, (3) virtual method calls are slow, (4) it generates too many type definitions. (2) is false: OO runtimes are optimized to access private fields very fast; furthermore, accessing an array, as I do, with a constant index value gets compiled into a direct access with a little indirection. (3) is false: virtual calls are a few machine instructions more expensive than a static call and only matter when a virtual method is several layers up in the type hierarchy. (4) is true, but it''s impact on program performance is dubious. (1) is true, but closures are not usually allocated at a very fast clip like cons cells are.

Conventional closure conversion allocs a vector, where the first cell is a pointer to the function and the rest contain the environment values. The function is called with the vector passed in as the first argument. This is EXACTLY how instance methods work. The difference between a closure and object is that a object can contain an arbitrary number of function pointers and has runtime support to dispatch to the right method. Therefore, as the title of this post implies, an object is equal to and greater than a mere closure. The trick is to exploit objects in new ways to make closures execute faster.

Two simple things should improve the generated code with respect to (1) and (4). First, do everything possible to get things into tailcall form, which would compile into ordinary loops. Second, closures which do not escape their definition do not need to be allocated; instead, generate a private method within the same closure class. Both of these reduce the number of closures, i.e. types, and reduce the number of allocations. Mostly top-level definitions will be allocated (only once) and a few first-class functions. This should vastly improve things.

A combination of incremental lambda-lifting and parameter dropping can be used to get mutually recursive inner lambda forms to share the same signature. This way, tailcall, which is slow on .NET, can be replaced with a jump instruction. Local-CPS analysis, described by Reppy, can find more loops, which will be really fast. Also, propagating type information and fixing the method signatures should make private method calls more efficient by avoiding typechecks and box/unbox overhead. This is a tougher one for Scheme. If the damn inliner on .NET worked right, these optimizations would allow the runtime to inline hotspots and produce really fast code.

All this would apply to a single "define" form and all the internal lambdas. However, by wrapping a module system around a group of "define" forms, the analyses could be applied to the whole module. And if you run your Scheme progam in "unverified" mode, it could approach the speed of cleanly written C# code. That ain''t so bad.



Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by antareus
...including one very long post detailing why the garbage collector works the way it is, and why there isn''t deterministic finalization. Its worth reading.

Do you mean Brian Harry''s "Resource management": http://discuss.develop.com/archives/wa.exe?A2=ind0010A&L=DOTNET&P=R28572

Share this post


Link to post
Share on other sites
quote:
I''ve heard they got rid of many "useless" keywords but I personally love them as compiler errors are much better than run time.

Did they get rid of "virtual"? That''s one keyword that becomes useless in a runtime compiled environment where the environment knows exactely what classes are loaded and what methods are overridden. Doesn''t have to do with compile-time vs run-time errors either.

Share this post


Link to post
Share on other sites
::Doesn''t have to do with compile-time vs run-time errors either.

It HAS - methods without the virtual keyword can NOT be overridden. It turns into a security issue. You can selectively allow subclasses (in other projects, for example) to override your methods, or you dont allow it.

Regards

Thomas Tomiczek
THONA Consulting Ltd.
(Microsoft MVP C#/.NET)

Share this post


Link to post
Share on other sites
For overloading you have:

interface (like a pure abstract class from C++, but also works for COM interop -- easier than IDL in C++ COM)

abstract (class) (class cannot be instanciated, but can have function bodies defines)

sealed (class) (class cannot be inherited)

abstract (function) (must be overloaded by "override", you used =0 in C++ to do this)
virtual (function) (can be overloaded by "override")
new (function) (if you want to overload a non-virtual function)
override (function) (to overload a virtual function)

For operator overloading, you have:

implicit (typecast operator will automatically happen if it needs to)
explicit (casting must be done manually)


classes can now only inherit from one base class, but can also inherit from any number of interfaces.


examples:


public interface IFoo
{
void FuncA(int blarg);
}

public interface IBar
{
string[] FuncB();
}

public class Whee
{
public virtual int Skydive()
{
// dostuff
}

public abstract int Slide()
{
// dostuff
}

public int Fly()
{
// dostuff
}
}

public class Blarg : IFoo, IBar, Whee
{
public void FuncA(int blarg) { } // compiler will yell at you if you forget to implement these
public string[] FuncB() { }

public override int Skydive() { } // optional

public override int Slide() { } // required

public new int Fly() { } // optional, only called if type is Blarg at compile time (won't call this if the type is Whee).
}


public sealed class Jar
{
...
}

public class Can : Jar // compiler error
{
}



One important reason why all this craziness is in there is because you have the ability to overload any of the non-sealed .NET Framework classes, (or classes in any assembly (read ".DLL") that you include in your project)

On many occasions the Framework classes will be sealed if it wouldn't really make sense to ever override them.


Also, good news: They're working on C# "generics" that will work just like good ol' C++ templates. When will you be able to use them in a release version? Probably not for a while...

[edited by - Nypyren on August 12, 2003 8:49:52 PM]

Share this post


Link to post
Share on other sites
quote:
It HAS - methods without the virtual keyword can NOT be overridden. It turns into a security issue. You can selectively allow subclasses (in other projects, for example) to override your methods, or you dont allow it.

Well, I assumed the existence of the 'final' keyword (does that exist in C#?). I guess that adds another keyword though, DOH!.

Anyway, final may be useful even when you have virtual since it works on classes and, correct me if I'm wrong, you will be able to override a method without the virtual declaration if it has been defined as virtual in a superclass.

[edited by - HenryAPe on August 14, 2003 10:31:46 AM]

Share this post


Link to post
Share on other sites
So.. C# has ''const'', ''sealed'' and ''virtual'' to do the jobs that are done by just ''final'' in Java. Not very clever, but I guess they didn''t want to be identical.

Share this post


Link to post
Share on other sites
It may have something to do with implementation of those type of keywords in the other .NET langauges and hence the common MSIL. It also may make the code more clear.




VSEDebug Visual Studio.NET Add-In. Enhances debugging in ways never thought possible.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
C++ has const and virtual, which are identical to what C# const and virtual do. Does that mean C++ isn''t very clever either? All sealed means is that a class is not inheritable. It makes sense to me *shrug*

Share this post


Link to post
Share on other sites
Actually, I think 'const' is a good thing, just not that sure about 'sealed' and 'virtual'. At least 'virtual' seems unnecessary if you have final (is sealed the same thing?) and uses a compilation model where the compiler can determine exactely what classes are overloaded at the time of compilation.

[edited by - HenryAPe on August 14, 2003 3:20:28 PM]

Share this post


Link to post
Share on other sites
Heh, all you guys forgot to mention the override keyword...

quote:
and uses a compilation model where the compiler can determine exactely what classes are overloaded at the time of compilation.


The issue is versioning. Requiring virtual in a base class is a failsafe against class interfaces breaking when new versions of the base class is introduced. Consider the following scenario:

The GoodSoftware company releases the VeryUseful class as part of a library:

public class VeryUseful
{
public void CleverMethod();
}

The CleverDevelopers company decides to use that library, and they use the VeryUseful class by deriving from it. They also add a new method:

public class EvenMoreUseful : VeryUseful
{
public void EvenMoreCleverMethod();
}

Now everything is nice and dandy. However, one year later the GoodSoftware has the same idea that CleverDevelopers Inc had - they add the EvenMoreCleverMethod();

public class VeryUseful
{
public void CleverMethod();
public void EvenMoreCleverMethod();
}

CleverDevelopers Inc''s EvenMoreUseful class will now most likely start to behave in very strange ways, since EvenMoreCleverMethod() is overridden in a way that the base class designers never intended it to be. And you will have no warning from the compiler.

C#''s requirement to explicitly state virtual and new/override avoids the above scenario.



AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.

Share this post


Link to post
Share on other sites
I''m not too familiar with Java, but doesn''t the final keyword mean it can''t be derived.... So how is that supposed to replace virtual which means it can be derived? As for sealed/final from my understanding its the same idea... Your making a contract saying "this class cannot be derived from".

Pat - Ex nihilo nihilus

Share this post


Link to post
Share on other sites
quote:
Original post by patindahat
I''m not too familiar with Java, but doesn''t the final keyword mean it can''t be derived....


It is also overloaded to mean pretty much the same as readonly in C#, which is one variation on const in C++(const pointer to non-constant object).
quote:

So how is that supposed to replace virtual which means it can be derived? As for sealed/final from my understanding its the same idea... Your making a contract saying "this class cannot be derived from".

Yes, and by implication, it would then be non-virtual. If you cannot derive from a class, there is no need for it''s methods to be virtual. Java by default assumes that all non-private methods are virtual, unless they are declared final or they are in a class that is final.



AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.

Share this post


Link to post
Share on other sites
::Java by default assumes that all non-private methods are
::virtual

This is exactly the assumption Anders Heilsberg wanted not to have. This CAN be dangerous.

So he demanded the virtual / override pair to make sure everyone involved knows what happens.

Same with ref and out parameters. Need to be declared on both sides.


Regards

Thomas Tomiczek
THONA Consulting Ltd.
(Microsoft MVP C#/.NET)

Share this post


Link to post
Share on other sites