12
返回列表 发新帖
楼主: Sky-Tiger

Java (J2SE 5.0) and C# Comparison

[复制链接]
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
11#
 楼主| 发表于 2008-10-26 13:07 | 只看该作者
20. Assemblies, Namespaces & Access Levels

In C#, you can organize the components of your source-code (classes, structs, delegates, enums, etc.) into files, namespaces & assemblies.

Namespaces are nothing but syntactic sugar for long class names. For instance, rather than calling a class Genamics.WinForms.Grid you can declare the class as Grid and enclose the class with:

namespace Genamics.WinForms {
    public class Grid {
        ....
    }
}
For classes which use Grid, you can import it using the "using" keyword instead of referring to its full class name Genamics.WinForms.Grid.

Assemblies are .exes or .dlls generated from compiling a project of files. The .NET runtime uses the configurable attributes and versioning rules built into assemblies to greatly simplify deployment - no more hacking the registry - just copy the assembly into a directory and it goes. Assemblies also form a type-boundary to deal with type-name collisions, to the extent that multiple versions of an assembly can co-exist in the same process. Each file can contain multiple classes and multiple namespaces. A namespace may also be spread across several assemblies, so there is high level of freedom with this system.

There are five access levels in C#, private, internal, protected, internal protected, and public. Private and public have the same meanings as in Java, noting that in C# the default access level is private, rather than package. Internal access is scoped to assemblies rather than namespaces (which would be more analogous to Java). Internal protected access is equivalent to Java's protected access, and protected access is equivalent to Java's private protected access made obsolete in Java some time ago.

21. Pointer Arithmetic

Pointer arithmetic can be performed in C# within methods marked with the unsafe modifier. When pointers point to garbage collected objects, the compiler enforces the use of the fixed word to pin the object. This is because garbage collectors rely on moving objects around to reclaim memory, but if this happens when you're dealing with raw pointers you'll be pointing at garbage. The choice of the word "unsafe" I believe is well chosen since it discourages developers from using pointers unless they really need to.

22. Rectangular Arrays

C# allows both jagged and rectangular arrays to be created. Jagged arrays are pretty much the same as Java arrays. Rectangular arrays allow a more efficient and accurate representation for certain problems. An example of such an array would be:

int [,,] array = new int [3, 4, 5]; // creates 1 array
int [1,1,1] = 5;
Using jagged arrays:

int [][][] array = new int [3][4][5]; // creates 1+3+12=16 arrays
int [1][1][1] = 5;
In combination with structs, C# can provide a level of efficiency making it a good choice for areas such as graphics and mathematics.

23. Constructors and Destructors

You can specify optional constructor parameters:

class Test
{
    public Test () : this (0, null) {}
    public Test (int x, object o) {
    }
}
You can specify static constructors:

class Test
{
    static int[] ascendingArray = new int [100];

    static Test () {
        for (int i = 0; i < ascendingArray.Length; i++)
            ascendingArray = i;
    }
}
Destructors are specified with the C++ naming convention using the ~ symbol. Destructors can only be made for references types and not value types, and cannot be overridden. A destructor can not be explicitly called, since it doesn't make much sense to do so when an object's lifetime is managed by the garbage collector. Each destructor in an object's hierarchy (from the most derived to the least derived) is called before the memory for the object is reclaimed.

Despite the naming similarity with C++, destructors in C# are much more like Java finalizers. This is because they are called by the garbage collector rather than explicitly called by the programmer. Furthermore, like Java finalizers, they cannot be guaranteed to be called in all circumstances (this always shocks everyone when they first discover this). If you are used to programming with deterministic finalization (you know when an object's destructor is called), then you have to adapt to a different model when moving to Java or C#. The model Microsoft recommends and implements throughout the .NET framework is the dispose pattern, whereby you define a dispose() method to for classes which manage foreign resources such as graphics handles or database connections. For distributed programming, the .NET framework provides a leased based model to improve upon DCOM reference counting.

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
12#
 楼主| 发表于 2008-10-26 13:08 | 只看该作者
24. Managed Execution Environments

The comparison between [C#/IL Code/CLR] and [Java/Byte-Code/JVM] is an inevitable and valid comparison to make. I thought the best way to make sense of these comparisons was to explain why these technologies were made in the first place.

With C and C++ programs, you generally compile the source code to assembly language code which will only run on a particular processor and a particular OS. The compiler needs to know which processor it is targeting, because processors vary in their instruction sets. The compiler also needs to know what OS it is targeting, because OSs vary in ways such as how executables work and how they implement some basic C/C++ constructs such as allocating memory. This C/C++ model has been very successful (most software you're using probably has been compiled like this), but has its limitations:

Does not provide a program with a very rich interface to interact with other programs (Microsoft's COM was built to address this limitation)
Does not allow a program to be distributed in a form which can be used as is on different platforms
Does not allow a program's execution to be limited to a sand box of safe operations
Java addressed these problems in the same way Smalltalk had done before it by having Java compile to byte-code which then runs on a virtual machine. Byte-code maintains the basic structure of a program before it is compiled, thus making it possible for a Java program to richly interact with another program. Byte-code is also machine-independent, which means the same class file can be used on a number of platforms. Finally, the fact that the Java language did not have explicit memory manipulation (via pointers) made it well suited to writing sand-boxed programs.

Originally virtual machines had an interpreter which converted a stream of byte-code instructions to machine code on the fly, but this dreadfully slow process was never appealing to the performance conscious programmer. Today most Java Virtual Machines use a Just-In-Time compiler which basically compiles to machine-code class skeletons just before they enter scope and method bodies just before they are executed. It is also possible to convert a Java program to assembly language before it runs to eliminate the overhead in start-up time and memory of a Just-In-Time compiler. As with compiling a Visual C++ program, this process does not necessarily remove the program's dependence on a runtime. The Java runtime (also covered by the term "Java Virtual Machine") will handle many vital parts of the programs execution such as garbage collection and security. This runtime is referred to as a managed execution environment.

Though somewhat obscured by terminology, the basic model .NET uses is the same as described above, though it was built to never use intepreting. Important improvements .NET will offer will come from the IL design itself, since the only way Java can match such improvements is by changing the byte-code specification which would generate serious compatibility issues. I don't want to discuss in detail these improvements - it should be left to those rare developers who understand both byte-code and IL-code. For the 99% of developers like myself who realistically aren't going to be studying the IL-Code specification, here are some of the design decisions for IL which are intended to improve upon byte-code:

To provide greater type neutrality (helps implementing templates)
To provide greater language neutrality
To always be compiled to assembly language before executing, and never interpreted
To allow additional declarative information to be added to classes, methods, etc., see 15. Attributes
Currently the CLR has yet to provide multi-OS support, but provides greater interoperability to JVMs in other areas (See 26. Interoperability)

25. Libraries

A language is pretty much useless without libraries. C# has remarkably few core libraries, but utilizes the libraries of the .NET framework (some of which were built with C#). This article is mainly concerned with addressing the C# language in particular, rather than on .NET, which is best dealt with in a separate article. Briefly, the .NET libraries come with a rich set of libraries including Threading, Collection, XML, ADO+, ASP+, GDI+ & WinForms libraries. Some of these libraries are cross-platform, while others are Windows dependent, but see the next section for a discussion on platform support.

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
13#
 楼主| 发表于 2008-10-26 13:08 | 只看该作者
26. Interoperability

I thought it would be useful to group interoperability into three divisions: Language interoperability, Platform interoperability, and Standards interoperability. While Java has its defining strength in platform interoperability, C# has it's strength in language interoperability. Both have strengths and weaknesses in standards interoperability.

Language Interoperability:

This is the level and ease of integration with other languages. Both the Java Virtual Machine and the Common Language Runtime allow you to write code in many different languages, so long as they compile to byte code or IL code respectively. However, the .NET platform has done much more than just allow other languages to be compiled to IL code. NET allows multiple languages to freely share and extend each others libraries to a great extent. For instance, an Eiffel or Visual Basic programmer could import a C# class, override a virtual method of that class, and the C# object would now use the Visual Basic method (polymorphism). In case you were wondering, VB.NET has been massively upgraded (at the expense of compatibility with VB6) to have modern object oriented features.

Languages written for .NET will generally plug into the Visual Studio.NET environment and use the same RAD frameworks if needed, thus overcoming the "second rate citizen" effect of using another language.

C# provides P/Invoke, which is a much simpler (no-dlls) way to interact with C code than Java's JNI. This feature is very similar to J/Direct, which is a feature of Microsoft Visual J++.

Platform Interoperability:

Generally this means OS interoperability, but over the last few years the internet browser has emerged as a platform in itself.

C# code runs in a managed execution environment, which is the most important technological step to making C# run on different operating systems. However, some of the .NET libraries are based on Windows, particularly the WinForms library which depends on the nitty gritty details of the Windows API. There is a project to port the Windows API to Unix systems, but this isn't here now and Microsoft have not given any firm indication of their intentions in this area.

However, Microsoft hasn't ignored platform interoperability. The .NET libraries provide extensive capabilities to write HTML/DHTML solutions. For solutions which can be implemented with a HTML/DHTML client, C#/.NET is a good choice. For cross-platform projects which require a more complex client interface, Java is a good choice. Kylix, a version of Delphi which allows the same code to compile to both Windows and Linux may also be a good choice for rich cross-platform solutions in the future.

Microsoft has submitted the C# specification as well as parts of the .NET specification to the ECMA standards body.

Standards Interoperability:

These are all the standards like databases systems, graphics libraries, internet protocols, and object communication standards like COM and CORBA, that the language can access. Since Microsoft owns or plays a big role in defining many of these standards, they are in a very good position to support them. They of course have business motivations (I'm not saying they are or are not justified) to provide less support for standards which compete with their own - for instance - CORBA competes with COM and OpenGL competes with DirectX. Similarly, Sun's business motivations (again I'm not saying they are or are not justified) means Java doesn't provide as good support for Microsoft standards as it could.

C# objects, since they are implemented as .NET objects, are automatically exposed as COM objects. C# thus has the ability to expose COM objects as well as to use COM objects. This will allow the huge base of COM code to be integrate with C# projects. .NET is a framework which can eventually replace COM - but there is so much deployed COM code that by the time this happens I'm sure .NET will be replaced by the next wave of technology. Anyway, expect .NET to have a long and interesting history!

27. Conclusion

I hope this has given you a feel for where C# stands in relation to Java and C++. Overall, I believe C# provides greater expressiveness and is more suited to writing performance-critical code than Java, while sharing Java's elegance and simplicity, which makes both much more appealing than C++.

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
14#
 楼主| 发表于 2008-10-26 13:08 | 只看该作者
Three new languages, not one
Visual Studio.NET actually includes three new languages rather than one. C# was built from the ground up as a new language. The two existing Visual Studio languages, C++ and Visual Basic, have been given extensive makeovers. (This is the last you'll hear about Visual BASIC in this article.)

In theory, the idea behind these extensions is that all of the languages in Visual Studio will use the "Common Language Runtime" (CLR). There are two components to this, the IL ("Intruction Language" bytecode (akin to the JVM) and the ".NET Framework". The first is a bytecode language akin to the JVM. The second is an API for controlling the Windows operating system and user interface. The goal is to give programmers in any supported language access to the same APIs. These two things are essentially independent, or at least you can use the IL bytecode without using the .NET framework.

Getting a C++ program to use the IL bytecode is fairly easy. Most C++ programs can simply be compiled to use it with the /clr option. Getting C++ to use the .NET Framework is a bit more difficult in that it requires that you use "Managed C++". "Managed C++" is a garbage collected, safety checked version of C++ that is not backward compatible with standard ("unmanaged" C++. Managed C++ code cannot use many standard C++ features, the most important of which are templates or any of the standard libraries. Making it all a bit confusing is the fact that you can mix unmanaged and managed C++ code. You can call managed code from unmanaged code, but not vice-versa.

In order to compare these various languages I took a program written for the old "Programming Fun Challenge 4" contest that I had written in C++, and rewrote it three times. First, in C#, then in Java (because C# is also supposed to be a "Java killer" and finally in "Managed C++". My goal was to get a sense of both the syntax and the performance differences of the four languages. (I say four, because as you'll see, "managed C++" ends up being practically an entire new language in its own.)

The program

Here is a Description of the PFC4.

The PFC4 program is not a perfect test, but I think it is a decent one. It ignores the GUI entirely, which is good as the differences between the different UI APIs would make a comparison near impossible. It does, however, use the collection classes extensively, so it isn't just a test of the raw language, but of the APIs as well.

All of these programs use an identical algorithm, differing only as much as the language required. The four programs can be found here:

The C++ version The C# version The Java version The Managed C++ version

I have attempted to use all of the same method and property names in order to simplify comparisons. Also, my naming conventions are all taken from the original C++ version and therefore may not match the normal conventions for C# or Java. This is intentional as it also simplifies comparison.

Caveat: My experience in both Java and C# is light. It is very possible that my implementations in either of those languages is substandard. Also, this program does not hit every portion of the language, so this article is only a view of the languages used to attack a particular sort of problem.

The Implementations

Here is the implementation of one of the simpler methods in each of the languages:

// c++
WORDHDL AddHandle(const string& aWord)
{
    int rc = GetHandle(aWord);
    if( rc != -1 )
        return rc;

    myWordsLookup[aWord] = myWords.size();
    myWords.push_back(aWord);
    return myWords.size()-1;
}

// Java
public int AddHandle(String aWord)
{
    int rc = GetHandle(aWord);
    if( rc != -1 )
        return rc;

    myWordsLookup.put(aWord,new Integer(myWords.size()));
    myWords.add(aWord);
    return myWords.size()-1;
}

// C#
public int AddHandle(string aWord)
{
    int rc = GetHandle(aWord);
    if( rc != -1 )
        return rc;

    myWordsLookup[aWord] = myWords.Count;
    myWords.Add(aWord);
    return myWords.Count - 1;
}

// Managed C++
WORDHDL AddHandle(String *aWord)
{
    WORDHDL rc = GetHandle(aWord);
    if( rc != -1 )
        return rc;

    myWordsLookup->set_Item(aWord, __box(myWords->get_Count()));
    myWords->Add(aWord);
    return myWords->get_Count()-1;
}


Some things to notice:


Java doesn't have anything like typedef, ie, something to make a quick-and-dirty alias for a type. This is a shame, because as you can see here, this feature can be used for code clarity. For the Java version, I have to remember that certain methods that return int are returning a handle to a word string.
Java requires you to explicitly wrap base types when you put them in a container. Managed C++ requires you to "box" a base type when putting it in a container. (This is essentially the same thing). C# implicitly does these for you. C++'s containers are not object-based, so no casting or wrapping is needed.
Both C++ and C# make use of operator overloading to simplify the syntax of container inserts and fetches. Note that while you can in theory do this in Managed C++, the .NET framework does not do this, making inserts and features a bit uglier. Foo[key] = data; becomes Foo->set_Item(Key, __box(Data));.
Note the way C# muddies the difference between a property and a method. The Count property in the C# version is the same as the Count() method in the Java version. In other words, it could involve executing arbitrary amounts of code.
Thoughts about C#

Despite its name, C# is much more of an extended Java than an extended C++. The similarities are extensive, going as far is using identical names for identical methods. Comparing the two languages themselves, I'd have to say that C# is just slightly easier to work with in that where it doesn't mimic Java exactly, it extends it in a useful way. The two major extensions that I ran into were the ability to define properties with code (which I found useful syntactic sugar though I know others object to the idea) and the simpler syntax due to more implicit casting.

Thoughts about Managed C++.

It should be clear from anyone looking at the managed C++ version that it is not a viable option for new development. It is by far the longest of the programs, and also, in my opinion, the most syntactically ugly of the four. And indeed, Microsoft explicitly discourages its use for new development, promoting it mainly as a way to pull in old C++ code. And as you'll see below, you don't even get the performance benefits you'd expect using C++, so there seems to be little reason to ever use as anything other than an upgrade path.

Collection containers

The C++ containers are different from the containers in both Java and in the .NET Framework in that they are generic containers rather than object oriented containers. In other words, for C++, you say "I want a container to put things of type Foo" in whereas in the other language you say "I want a container to put things in".

None of the languages provided all of the containers I wanted for this application. C++ is missing a good hashtable type. (std::map is a b-tree.) C# is missing any concept of a set (i.e. a container where the key and the data are the same). Neither C# nor Java have containers with non-unique keys. (Though they are pretty easy to fake with containers of containers.)

The SGI implementation of the C++ STL has a nonstandard "hash_map" extension. This version is very common in the C++ community and is rumored to be slated for inclusion of the next C++ standard. It has an interesting effect on performance, so I included support for it in the C++ version.

Objective comparisons

The lengths of the different programs is interesting:

Table One

C++: 375 lines
C#: 425 lines
Java: 431 lines
Managed C++: 512 lines


I was a bit taken aback by the fact that the C++ version came in shorther give that C# and Java are supposedly "higher level" languages. Part of this is due to the STL and the fact that it has a quick-and-dirty tuple class (std:air) that I could use in containers to avoid having to create a special class for the same purpose. Also influencing this is that neither Java nor C# are as good at console IO as C++ is. This is not surprising given their GUI orientation and C++'s console heritage.

The timing differences are more interesting. a note on the many C++ versions. C++ can be compiled to use the CLR or not to use the CLR even if it is not managed. This has timing implications. There are also different versions using the various STL implementations, and, for kicks, versions compiled with gcc rather than Microsoft Visual Studio.NET.

All timings were obtained on a 800 Mhz Pentium IV using this input data. All times in seconds.

Table Two

Standard C++: 27.99
Standard C++ + SGI STL 11.15
Standard C++ + SGI STL and hash_map 6.04
g++ C++: 17.28
g++ C++ + SGI STL: 14.93
g++ C++ + SGI STL and hash_map: 7.29
Standard C++ compiled /clr: 34.36
Standard C++ + SGI STL compiled /clr: 25.09
Standard C++ + SGI STL and hash_map compiled /clr: 12.98
Managed C++: 111.59
C#: 93.08
Java: 65.57


As you can see, the differences can be substantial. The fastest and the slowest systems differed from each other by more than a factor of fifteen. In general, the C++ versions outperformed the others, with one very important exception; the very slowest of the systems was the managed C++ version.

Why? There are lots of possibilities. The most obvious would be the bytecode, but it is clear from the C++ versions compiled to use bytecode that the performance hit here is about a factor of two. This alone does not explain the performance differences between C++ and the other three languages.

An obvious culprit is the .NET Framework itself, as the two differences between the Managed C++ version and the standard C++ version are the .NET Framework and garbage collection. And indeed, after doing a little bit of profiling, it appeared that the .NET Framework classes performed very differently then the STL collection classes. In particular, the C++ classes were actually slower on inserts than the .NET Framework collection classes, but were far, far faster on fetches. (The C++ version that runs in six seconds spends almost a second and a half of its time loading the dictionary, something the managed C++ version does in less than a second.)

It is interesting that the Java version, while significantly faster than those using the .NET Framework, is still significantly slower than the C++ versions. This could, in theory, be the garbage collection, but I suspect not, as playing around with other aspects of C# programming, most notably, UI development, I've found it generally only to be only about half as slow as C++. I suspect the big difference is that the C# collections need to store full objects whereas the C++ collections can store base types.

Final Thoughts

One thing I've found when comparing C# to C++, both with this program and others, is that the biggest differences in the ease of programming come not from the languages themselves but from the APIs. The collection classes in C# (and Java) are substantially easier to use than the STL. The new .NET Framework is substantially easier to use then the older Win32 API for Windows programming. (And also quite a bit easier to use than Gtk.) These differences seem to swamp out a lot of differences in ease of use in the languages themselves. But it still must be said that programming in C# is certainly easier than programming in C++. There's a lot of fairly arcane stuff you must think about if you are going to get the most out of C++ whereas most of these decisions are made for you under the covers with C# (or, indeed, most languages).

It is also important to see that the performance differences between the various systems are very application dependent. The timings here make C# (and also Java) look very slow compared to C++, and while I chose the task essentially at random, in retrospect I think it was a bit unfair in that I suspect other tasks might not show such a performance gap. Certainly a benchmark that did something like calculate Fibonacci numbers, and that avoided the collection classes, would make C# look much better. It is also interesting to note the impact that simply using the SGI hash_map had on the C++ versions. It shows how important choosing the right data structure for the task is.

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
15#
 楼主| 发表于 2008-10-26 13:09 | 只看该作者

使用道具 举报

回复
论坛徽章:
131
乌索普
日期:2017-09-26 13:06:30马上加薪
日期:2014-11-22 01:34:242014年世界杯参赛球队: 尼日利亚
日期:2014-06-17 15:23:23马上有对象
日期:2014-05-11 19:35:172014年新春福章
日期:2014-04-04 16:16:58马上有对象
日期:2014-03-08 16:50:54马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14
16#
发表于 2008-10-26 23:42 | 只看该作者
靠, 又水这么多

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
17#
 楼主| 发表于 2008-10-27 10:28 | 只看该作者
总结的还是很全面的!很客观!

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表