Read PDF C++ CLI in Action (Manning)

Free download. Book file PDF easily for everyone and every device. You can download and read online C++ CLI in Action (Manning) file PDF Book only if you are registered here. And also you can download or read online all Book PDF file that related with C++ CLI in Action (Manning) book. Happy reading C++ CLI in Action (Manning) Bookeveryone. Download file Free Book PDF C++ CLI in Action (Manning) at Complete PDF Library. This Book have some digital formats such us :paperbook, ebook, kindle, epub, fb2 and another formats. Here is The CompletePDF Book Library. It's free to register here to get Book file PDF C++ CLI in Action (Manning) Pocket Guide.

Create your GUI app as a. Depends on if you want to learn another language? Anyways, if it's a trivial app or program you are porting it's not that hard using WinForms. Actually, it's just a bit harder than using C since you get to use the same GUI editor in Visual Studio but you have to write a lot more of the code by hand than using C. Deitel, Harvey M. Use Stack Overflow for Teams at work to find answers in a private and secure environment. Get your first 10 users free.

Sign up. Learn more. First 10 Free. Asked 9 years, 4 months ago. Active 9 years, 4 months ago.

  • Part 1 The C++/CLI Language.
  • 買這商品的人也買了....
  • Maiden and Mother: Prayers, Hymns, Devotions, and Songs to the Beloved Virgin Mary Throughout the Year?
  • Osprey Campaign 062 - Pearl Harbor 1941 Day of Infamy.

Viewed 9k times. So can I use. If so, How? Please be clear. Add to Basket. Book Description Manning Publications, Condition: New. Never used!. Seller Inventory P More information about this seller Contact this seller. Brand New!. Seller Inventory VIB Seller Inventory M Ships with Tracking Number! Buy with confidence, excellent customer service!. Seller Inventory n. Here s something to watch out for when using for each loops. What happened? The operative word is copy. When you gcnew the String, you assign it to this local handle and not to the array element.

This property of CLI arrays is called array covariance. Note that array covariance is limited to arrays of ref types. To illustrate this using a simple example, think of a School class that has an AddStudents method that takes an array of Student objects as an argument. There might be specialized students in that school, such as math students represented by the MathStudent class, which is derived from Student or biology students represented by the BiologyStudent class, again derived from Student.

Had you used System::Array as the argument to AddStudents, nothing would stop a slipshod programmer from passing an array of String objects to the AddStudents method Arrays of non-cli objects A CLI array s element can t be a native type, but a pointer to a native type is allowed.

Although it probably won t be a common scenario, there may be times when you want to store native objects in a managed array; at such times, this technique can be handy. When the array arr goes out of scope and gets garbagecollected, its member elements don t get cleaned up.

Pdf C Cli In Action Manning

This means you have a memory leak. If you run this code, you ll see the following output:. In this class, you put the deletion code in the destructor. You should use stack semantics when declaring objects of type R to take advantage of deterministic destruction when the object goes out of scope. If this doesn t make sense now, don t worry; the next chapter discusses stack semantics, destructors, and finalizers in detail.

When R s b. Let s look at how you can directly manipulate the contents of an array using native pointers Directly accessing CLI arrays using native pointers Sometimes, it s good to have direct access to an array through a pointer. This gives you a fast access mechanism to manipulate the array, because you sidestep the CLR s array-access mechanism, which isn t as performant as direct pointer access. The example in listing 2. For now, understand that once an object s address is assigned to a pinning pointer, the GC knows that it should not move that object around in the CLR heap.

The pinning pointer takes. You also initialize a pointer plast to point to memory just after the last element in the array. Now that you have pointers to the beginning and end of the array, you use regular pointer arithmetic to increment each element in the array d.

  • Manning Ebook Collection | Board4All.
  • Where Darwin Meets the Bible : Creationists and Evolutionists in America;
  • Créez un blog gratuitement et rapidement sur Free!;

The output of the code snippet shows that each array element has been incremented by 1, proving that the native pointer manipulated the CLI array, which is what you set out to do. In general, unless you have specific reasons to write high-performant code, perhaps inside a frequently accessed code block that manipulates a substantially sized array, it s better to use CLI array semantics to access the array, because you don t run the risk of doing incorrect pointer math and corrupting memory. You also get all the benefits of CLI type-safety. Another problem with using pointers to access arrays is that you inherently have to pin the array first, and this may cause heap fragmentation problems.

I ll talk more about this later in this book when I cover pinning pointers. With that we come to the end of our coverage of CLI arrays. We have discussed basic array concepts, the usage of single-dimensional, multidimensional. That s a lot of stuff, and you may want to grab a cup of coffee to soothe your aching brain for a while. On a few occasions, we encountered concepts we haven t covered yet, such as pinning pointers and CLI destructors, but we ll discuss those topics in later chapters.

Pearson Education - C++/CLI in Action

They are essentially. You ll encounter them frequently when you re doing managed or mixed-mode coding, which makes it essential that you know how they work and how to use them. At this point, we are almost, but not quite, ready to jump into mixed-mode programming. We ll do that in the next chapter. We ll start with stack semantics and deterministic destruction, which overcomes the issue of nondeterministic finalization that s prevalent in a garbagecollected environment; we ll also take a brief look into the garbage-collection algorithm used by the CLR.

We ll look at how CLI generics compares with templates, where each mechanism is useful, and how you can mix them for maximum flexibility and value. By the end of this chapter, you should be set for diving into the mixedmode programming techniques that will be covered in the rest of this book. Microsoft alternatively offered the Dispose pattern, where classes had to implement an IDisposable interface and manually call Dispose on their objects when they went out of scope. The problem was that this required the programmer to manually and consistently call Dispose on an object whenever the object needed to be finalized.

The situation became worse when the object had managed member objects that themselves would. The destructor is deterministically invoked, thereby satisfying the RAII idiom. In this section, we ll discuss the new destructor and finalizer semantics, take a brief look at the. The destructor for a class is called when an object of that type is deleted manually using a call to delete, whereas the finalizer is called during garbage collection. A destructor is analogous to your throwing an empty soda can into the waste bin on your own; a finalizer is analogous to your dropping it on the floor, and a janitor picking it up and putting it into the waste bin during one of their cleaning cycles.

For automatic variables that use stack semantics discussed later in this chapter , the destructor is automatically called when the object goes out of scope. Destructors provide a deterministic mechanism to clean up an object, whereas a finalizer provides a nondeterministic mechanism to clean up an object when it gets garbage-collected. Typename Listing 3. For r2, however, you don t manually call delete, which means the Garbage Collector GC cleans it up during the next garbage-collection cycle.

Because this is a small program, the GC comes into play only when the program has exited its main function. This explains why you see R::! R displayed after main has exited in the program output. The GC doesn t call a finalizer for the r1 object because the destructor has already been called for it. Before we look at how the compiler generates MSIL for destructors and finalizers, you need to understand the basic concepts of.

These objects are automatically freed by the. The GC is analogous to the janitor we talked about earlier, who cleans up after you, picking up garbage and dumping it into the waste bin. As a developer, this means you only need to allocate objects. You don t have to manually free those objects; that is done for you by the CLR. You may wonder how the GC knows that an object is no longer in use and can be freed up.

That s what we ll talk about in this section. I ll keep the discussion as brief and simple as I can, because I only want you to understand how objects are freed by the GC and when finalizers are called. For a more detailed study of the. When a. It keeps a pointer to the next available free space. I ll refer to it as pnextobject in this discussion.

Initially, pnextobject points to the beginning of the managed heap. As new objects are created, pnextobject is incremented to point to the beginning of the free memory block. Figure 3. Whenever a new object is to be created, the CLR needn t hunt for a free block of memory: It creates the object starting at the memory location pointed to by pnextobject.

It then increments pnextobject by the size of the just-created object. This is why memory allocation is extremely fast and efficient in. If RAM was infinite, the. Unfortunately, RAM is not only finite, but its finiteness can also be conspicuous when you start developing real-world applications! What happens when you run out of space? When the managed heap is full and a new object needs to be created, a garbage-collection cycle is performed. Note that this isn t the only scenario in which a garbage-collection cycle can occur.

Before we look at what happens during a garbage-collection cycle, you need to understand the concept of roots. A root is a memory location that is essentially a reference to a managed object on the heap or a nullptr. All global and static objects are part of a. Other roots include currently active stack variables in any thread of the application, as well as CPU registers that hold references to objects on the CLR heap. When a garbage-collection cycle occurs, the GC assumes that the entire managed heap is garbage and starts building a list of reachable objects by walking through its list of roots.

An object is deemed reachable if it can be accessed directly or indirectly through a root. For example, a root may point to an object A that may have a member that points to an object B. Although B can t be directly reached from that root, it can be indirectly reached via object A. Thus it s deemed reachable. Object B is a reachable object, whereas objects C Figure 3. Once the GC has built up a list of reachable objects, every object in the managed heap that isn t in this list is considered garbage.

The GC proceeds to compact the heap by moving the reachable objects down the heap, so that it ends up with one contiguous block of memory. A diagrammatic representation of this is shown in figure 3. Of course, this invalidates every managed pointer that refers to any of the objects that were relocated during compaction; thus, the CLR updates all those references to point to the new memory locations.

This explains why interior pointers covered in the next chapter and managed reference handles aren t affected by garbage-collection cycles. Because they get updated by the CLR, they always refer to the original object. You can also understand why a native pointer, which knows nothing about the CLR, won t work with managed objects. A native pointer continues to point to where it was pointing to, regardless of whether the GC has relocated the object that was being pointed to.

That is the case because native pointers were not designed for a garbage-collected memory system such as the one the CLR provides. As far as a native pointer is concerned, the memory address it points to is fixed and never changes. If you want it to point to some other memory location, you need to manually change the pointer to point to that new memory address. Note that the GC won t relocate pinned objects pinning is covered in the next chapter; for now, just be aware that a pinned object remains in a fixed memory location for the duration of pinning.

It can t efficiently compact the heap when there are a substantial number of pinned objects. This leads to the heapfragmentation problem that we ll discuss in more detail in a later chapter, when we discuss pinning pointers. At the end of the heap-compaction operation, the GC updates the pnextobject pointer to point to the first free block of memory, so that the next allocation is done from that address.

You may wonder whether going through the entire managed heap every time you run out of memory is all that efficient. My answer is that it most definitely would be inefficient to do so. It makes better sense to classify the heap into hierarchical Figure 3. Apparently, the designers of the. In my explanation of the garbage-collection algorithm so far, I ve presumed that the managed heap is considered a single entity. In reality, however, the. All newly-allocated objects are put into Generation-0 of the heap.

A garbage-collection cycle is always performed on a generation rather than on the entire heap. When Generation-0 runs out of memory, a Gen-0 garbage-collection cycle takes place. At the end of the garbage-collection cycle, all remaining objects are promoted to Generation The assumption made here is that objects surviving a garbage-collection cycle are likely to be long-living objects.

They re then promoted to a higher generation so that they will be exposed to fewer garbage-collection cycles. Eventually, Generation-1 may run out of space, too. When that happens, a Gen-1 GC cycle occurs, and all remaining objects are promoted to Generation-2, the highest garbage-collection generation. Generation-2 garbage-collection cycles are rare; when they happen, objects that are still alive remain in Generation-2, because there is no higher generation. Typically, globals and static objects in a long-running application end up in Generation-2 and stay there throughout the life of the application.

Generation-1 contains medium-lifetime objects; garbage-collection cycles do occur in Gen-1, although they aren t frequent. Gen-0 contains all newly-allocated objects; garbagecollection cycles happen frequently in Gen If your head is feeling a tad heavy with all that information, don t lose sleep over it; the working of the GC is entirely transparent to you, the developer.

It s advantageous to have an idea of how it works, but it s not imperative to remember every implementation detail. With that happy thought, let s continue. The large object heap The CLR has a separate heap for allocating large objects: the large object heap. Objects that are around 80KB or more are allocated from the large object heap.

The large object heap isn t generational; during a garbage-collection cycle, objects aren t relocated, and there is no compaction. This is because moving large objects isn t efficient and because only large objects are allocated here, the risk of fragmentation is minimal. Except for the fact that the large object heap is nongenerational and doesn t perform memory compaction, it behaves exactly like the generational-managed heap.

Whenever you create an object that has a finalizer, a pointer to this object is added to a Finalization queue maintained by the CLR. During a garbage-collection cycle, once the garbage objects objects that don t have reachable roots have been identified, the GC checks to see if any of these objects are in the Finalization queue. If a garbage object is in the Finalization queue, the GC removes its entry from that queue and adds it to another queue called the F-Reachable queue, where the F stands for Finalization. Like a global or a static variable, the F-Reachable queue is considered to be a root.

The moment the GC moves the pointer to the object from the Finalization queue to the F-Reachable queue, the object is no longer garbage because it now has a reachable root. This process in which an object transitions from being garbage back to being reachable is called object resurrection. Because the object isn t garbage anymore, the GC promotes it to the next-higher generation. Every managed application has a special runtime thread that is responsible for calling finalizers. If the F-Reachable queue is empty, this thread remains dormant; but whenever objects are added to the F-Reachable queue, this thread wakes up and removes each object from the queue after calling its finalizer, until the queue is empty.

At this point, these objects have gone back to their unreachable state and will be cleaned up during the next garbage-collection cycle. As you can see, objects with finalizers aren t as efficient as those without, because at least two garbage-collection cycles are required to clean them up. Garbage-collection cycles are expensive operations; they consume valuable CPU cycles. The roots have to be traversed, the heap must be compacted, managed pointers to relocated objects have to updated, objects must be promoted to higher generations, finalizers have to be called, and pinned objects must be left alone.

Although having a garbage-collected memory manager is convenient, typically you ll find that it s less efficient than native memory management. As far as my experience goes, however, the generational garbage-collection algorithm developed by the CLR team is outstanding. If you code with a little understanding of how the GC works, you can minimize the performance hits associated with automatic memory management. You should now have a basic understanding of how the GC works and how finalizers are invoked.

IL implementation for destructors and finalizers In our coverage of garbage collection, you saw how finalizers are invoked automatically by the GC before objects are cleaned up. It s apparent that. But there was no mention of destructors. It then calls GC::SuppressFinalize this , which removes that object from the Finalization queue, because the object need not be finalized now that its destructor has been called. For any object that defines a finalizer! This compiler-generated Finalize method calls Dispose false so that the finalizer! The next example has a destructor as well as a finalizer; the compiler generates a Dispose method, a Dispose bool method, and a Finalize method.

If you examine the generated IL, you ll see that the structure of the generated class looks like listing 3.

Listing 3. In the generated class, Dispose b is the effective. Similarly, Finalize c is the effective finalizer that s called by the GC; it eventually invokes! When you call delete on a CLI object that has a destructor, the compiler generates a call to Dispose. You should now be comfortable with how destructors and finalizers are implemented by the compiler. Essentially, stack semantics lets you declare and use a ref type as an automatic variable.

In the handle version, if you didn t call delete, the object would have to wait for the next garbage-collection cycle before it gets finalized. Not only is this less efficient as we discussed earlier , but for objects that access scarce resources like file handles, it s important to execute clean-up code as early as possible. Stack semantics solves both these problems objects declared using stack semantics never need to be finalized because they re always destructed. They also need not be manually deleted, because the compiler generates the destructor call for you when they go out of scope.

Types that can t use stack semantics Three reference types can t use stack semantics: delegates, arrays, and System::String objects. These types are specially treated by the CLR, and the compiler can t generate destructors for them. It s a pity, really, because having to use handle variables for arrays and strings which are probably the two most commonly used types breaks code uniformity if you re using stack semantics for everything else ; but it s a CLR-imposed restriction and not the compiler s fault.

Let s write a function that opens two text files and writes the contents of both of those files to a new text file. In other words, it concatenates two text files to a third file. In the first version of the function, you manually clean up the writer and reader objects; see listing 3. This ensures that if an exception is thrown, any previously created objects are cleaned up. Compare that with the version that uses stack semantics, in listing 3.

Notice that you don t call delete. Instead, you let the objects get destructed when they go out of scope, which as illustrated in listing 3. That should help you appreciate how stack semantics brings a remarkable level of convenience to managed coding. Implementing stack semantics Consider that you have a function that uses a StreamReader object to read one line from a file. The compiler generates the necessary try-finally blocks and the calls to Dispose in the generated MSIL see table 3.

If you think about it, the compiler is writing code for you that you d normally have to write on your own. This code may feel wrong because it looks as if there will be double calls to delete when an exception is thrown. However, the MSIL uses fault handlers and not finally handlers to do this. If an exception is thrown, the code in the catch fault block executes, and then control leaves the block meaning the second delete never executes in such a scenario. A fault handler is similar to a finally handler, except that the fault block is executed only if its associated try block throws an exception.

The code the compiler generates is perfect; each object is deleted when it would normally have gone out of scope, and proper try-catch blocks ensure that only those objects are destroyed that need to be. In short, the compiler has not only saved you time, but it has also generated optimized code that would have been difficult to emulate had you tried to code it on your own.

The moment I added an additional disposable object, the code complexity increased substantially. Try to imagine how complex it would be with functions that have half a dozen or more disposable objects, with some of them possibly used in conditional blocks; I dare say even the most experienced developers would find it tedious and difficult to get it all right, not to mention how long it would take.

If your class needs to have member objects that are themselves disposable, you can declare them using stack semantics. The compiler will generate the required creation and disposal code for you. Handling member objects Think of a custom file reader class let s call it MyFileReader that uses a custom file stream object let s call it CustomFileStream to read from a special device.

آمار وبلاگ

Assume that both these classes use scarce resources and need to be disposed of deterministically. All you need to do is to declare a CustomFileStream member. With stack semantics on ref types, you get to have all the advantages of an automatic garbage-collected memory management system, as well as enjoy the benefits of the RAII paradigm.

Using the RAII model, stack objects follow scoping rules and destructors get called when an object goes out of scope. Note that the compiler doesn t allow you to declare a method for a ref class that has the same signature as one of the compiler-generated Dispose overloads. You also can t call Dispose directly on a ref object handle. Instead, you have to either use delete or change your code to use stack semantics Guidelines for using destructors and stack semantics Here are some general strategies that you can exercise to get the best out of using stack semantics.

Like all suggestions, they depend on your situation and therefore should be used only after you consider your specific coding scenario! Whenever possible, prefer a destructor to a finalizer. A finalizer is more expensive than a destructor, because the GC always promotes finalizable objects to a higher generation. If they have a pending finalizer, even short-lived objects will be promoted to Gen You also have little control over when the finalizer gets called, unlike with a destructor, which gets invoked deterministically.

This can be vital in cases where you have a resource that needs to be deallocated. For instance, imagine a network file stream class that writes some data to a network stream. Now, assume that the clean-up code that flushes the stream and closes the network connection socket is in a finalizer. The network connection has to be kept open until the finalizer gets executed sometime in the unpredictable future. Other applications that may need a network connection are forced to wait if all available.

Or, even worse, if the connection drops, there is a possibility of data loss, because you haven t flushed the contents of the stream yet. A destructor avoids all such issues. If your disposable class has a handle member that needs to be disposed, manually delete that member in the destructor. Consider a disposable class that has a member object that doesn t have a default constructor; that member can t be used as an automatic variable. Therefore, you re forced to use a handle to that object as the member. If you don t manually delete that member in the destructor, that member will be nondeterministically finalized during a future garbage-collection cycle, and you don t get the performance advantage or the predictability of using stack semantics.

The following code snippet demonstrates how you can do that. Once a destructor gets executed, because a call is made to GC::SuppressFinalize, the object is removed from the Finalization queue and its finalizer is never called. If there is some cleanup code in the finalizer that isn t present in the destructor, this may result in unpredictable behavior.

By calling the finalizer from the destructor, you ensure that it s definitely called. You may think you can work around this by putting the clean-up code in the finalizer into the destructor as well, but doing so results in code duplication. This makes changes and updates difficult because they have to be done twice, and in my opinion is best avoided. If a derived class method matches a base class method s name and signature, it implicitly overrides the base class method. In this section, you ll see how these new overriding features are implemented; we ll also look at how to declare and use sealed and abstract functions Explicit overriding Virtual functions in derived classes need to explicitly state whether they re overriding the base class method or not.

The compiler issues error C if you attempt an implicit override. Two keywords can be applied to a derived class virtual function: override means the function overrides the base class function. Consider listing 3. In the derived class,. For method Walk c, you specify the new modifier, which means this method doesn t override the base class method; rather, it hides the base class method and treats it as an independent virtual method. Later, when you create a HouseRobot object and assign it to a Robot handle, the call to Greet d will invoke the derived class method because it s an override for the base class method of the same name.

However, the call to Walk e invokes the base class method, because the method in the derived class is treated as an independent function although it has the same name and signature. In short, all you need to remember is that for virtual methods of ref types, you have to specify override or new if a derived class method has the same name and signature as the base class method. This gives you the flexibility of using appropriate names for derived class methods and at the same time overriding methods in the base class with a different name. When you invoke Greet e on a Robot handle that is a reference to a HouseRobot object, SayHello from the derived class gets invoked.

Similarly, when you invoke Walk f, Run from the derived class gets invoked. It s interesting and possibly confusing to a first-timer that the Walk method is present in both the base and derived classes, but the override for the base class Walk method is the derived class Run method. The ability to rename a method override in a derived class gives you a higher level of flexibility when designing complex class hierarchies. Renamed overriding can be extended so that a derived class function can override multiple methods, and that s what we ll look at next Multiple overriding Although ref types can t have multiple base classes, they can implement one or more interfaces in addition to having a base class.

Sometimes, you may want to have a function in a derived class that overrides both a base class method and an interface method. That s where multiple overriding comes into play. Let s continue with the robot class example and add an interface, IHumanoid, into the class hierarchy; see listing 3. In addition, it implements the interface IHumanoid.

Thus, multiple overriding is essentially a kind of renamed overriding, except that more than a single method is overridden. Similarly, when Talk is called on an IHumanoid handle holding the same HouseRobot object d, it s again HouseRobot s SayHello that is invoked Sealed and abstract functions A virtual function marked with the sealed modifier can t be overridden in a derived class. Consider the Robot class hierarchy example. Assume that a function named ShutDown is declared in the base Robot class; this function contains fundamental code to switch off a robot.

It s important that derived classes don t override this method, because if they re allowed to do that, and a derived class implements this function improperly, it could pose a security risk where a malfunctioning robot couldn t be switched off. That s where it s desirable to mark the ShutDown function as sealed, as shown in listing 3. Any attempt to do so results in a compiler error c. Note that the following code will compile: ref class HouseRobot : public Robot public: virtual void Greet override SHOW; virtual void Shutdown new SHOW; ; It compiles because here HouseRobot::ShutDown doesn t override the sealed function in the base class; instead, it creates an entirely new function, albeit with the same name.

Another modifier that can be applied to a function is abstract. An abstract function is equivalent to a pure virtual function, but using the abstract modifier allows for syntactic uniformity. The compiler generates identical MSIL for both styles. Therefore, it s up to you to decide what style to use. I suggest that you use the abstract modifier because it ensures syntactic uniformity with the rest of the function modifier syntaxes.

Also note how I marked the class Robot as an abstract class. If I hadn t done this, the compiler would issue warning C a class with abstract methods is not explicitly declared to be abstract. Even if you ignore the warning, the class will be marked as abstract in the generated MSIL. I recommend that you explicitly mark a class as abstract if it contains at least one abstract method.

If you haven t used templates before, you may not know what a parameterized type is. Put simply, a parameterized type is defined using one or more type parameters that are unknown until the type is first declared or defined. Once you see some sample code later in this and following sections, things will become more apparent. A generic remote control can work with any of these devices, if you specify which device you want to use it with this is analogous to the type parameter. There are some important differences in their behavior, compared to templates. In this section, we ll discuss both generics and managed templates, and also evaluate their differences.

Note that although both C and VB. I ll use the proverbial stack class to explain why you need parameterized types. If you ve used templates in the past, you may want to skip to the next section, where I describe the syntax for using CLI generics. Assume that for a specific application, you need to create three different stacks: one to store strings, one to store objects of a specific managed type, and one to store numbers. Not only is this pointlessly tiring to write, but the cast requirement means that the CLR must do runtime type-checking on the objects that are being cast, which affects the performance of the code.

When you use the stack to store numbers, you ll immediately notice the inefficiency introduced when using the Push and Pop operations. When you Push a number d, the int has to be boxed to an Object; when you Pop it back e, the Object has to be unboxed back to an int. Earlier, you saw that both boxing and unboxing are expensive operations that consume precious CPU cycles, thereby affecting code performance. Thus far, this class hasn t been impressive in terms of performance. But that s not all; the class isn t type-safe either.

The compiler compiles it fine; as far as it s concerned, the stack stores and retrieves Object items. During runtime, the previous code results in a runtime exception. In summary, not only is the stack class inefficient in terms of performance, but it s also deficient in enforcing type safety.

If parameterized types were not an option, the only way to get around these drawbacks would be to write three separate stack classes: one for an int, one for a String, and one for the custom type. Clearly, this is the wrong way to approach the problem. That s where parameterized types come into the picture; they solve the performance issues related to superfluous casting, and they also ensure strict type safety. In the next section, you ll see how CLI generics can be used to write a generic stack class that overcomes the drawbacks in the one you wrote in this section Generics syntax for classes and functions If you ve used templates before, you ll notice the remarkable similarity in syntax.

T Pop You also specify a parameterized type named T, using the typename keyword. Note that you aren t limited to one parameterized b. If you look at the array declaration c, you ll see that the type of the array is the parameterized type T. This type is known only when the generic class is first declared.

Similarly, the Push method d accepts a T object, and the Pop method e returns a T object. For the number stack d, the parameterized type is an int. Both Push and Pop accept and return an int, respectively, which means no boxing or unboxing is involved. Clearly, the generic version of the stack class has improved on performance by avoiding a d Int as parameterized type Figure 3. That s not all; generic classes also enforce type safety.

Similarly, for the number stack, Push expects an int, and thus it won t accept a string. You aren t restricted to generic classes. You can have a nongeneric class with one or more generic functions, and you can also have a global generic function. The class itself is nongeneric. In the previous snippet, the first time you call the function, you specify a float; the second time, you specify a char. Type parameter must be specified As you can see, the syntax for global generic functions is the same as that for a generic method of a nongeneric class. You may not find yourself using nongeneric classes with generic methods that often, but if you ever have to, it s good to know that it s possible. Similarly, global generic functions, which aren t portable with other languages like C or VB.