Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#

An Overview on Many Terms and Principles

Rate me:
Please Sign up or sign in to vote.
4.76/5 (27 votes)
7 Nov 2013CPOL23 min read 63.8K   85   62
Understand things like OOP, AOP, Loose-coupling, Class Inheritance, Interface Implementation, Open-Closed Principle and others.

Background

I see many articles talking about loose-coupling, many times also talking about interface implementation instead of class inheritance or simply saying that the right thing to do is to follow the open-closed principle.

Usually those terms are related, but in many places they seem to be the same thing or they have a definition that simply doesn't explain anything, so I decided to write an article about what each term means, giving personal explanations to help differentiate and relate them.

OOP - Object Oriented Programming

This is probably the most common term and the most confusing one for beginners. In the .NET world we don't have another option, we will inevitably create classes (or at least structs) and, if we instantiate them (that means, we do something like new MyClass()), we will be creating objects.

That's object oriented programming, right?

Well... that's something I hear a lot, and it is wrong. C has structs, which can hold data of specific types and give names to them. Yet, C is not considered object oriented.

If we look in Wikipedia, there's a definition that says: "In OOP, each object is capable of receiving messages, processing data, and sending messages to other objects."

Such definition, in my opinion, is imprecise. Maybe it is the "right" definition, but it doesn't really help beginners to understand the problem.

First, what is a message? Second, what's an object?

Some persons consider any method call, which may include parameters, to be a message. And an object? Well, any area of memory that you use consistently is an object. So, by such a definition, C is an object-oriented language, as we usually allocate structs and we have functions that manipulate them.

But if we go further in Wikipedia, it says: "...the object-oriented approach encourages the programmer to place data where it is not directly accessible by the rest of the program".

That's named encapsulation, and it is one of the things that separates OOP from non-OOP. In C, all struct fields are public. You may create your own personal rules to avoid accessing them directly, but the language itself will continue to allow it. In .NET, instead of making all fields public, fields should be private and they should only be accessed (be it to read or to write) by methods (properties are a kind of special method(s)). The purpose is to allow the internal implementation to be rewritten without requiring the callers to change. That is, if you change a field directly you will not execute a validation that may be required, you may not fill a secondary field that's calculated on it. But by using a "set" method, all the validations or secondary steps will be done, even if you don't know they exist.

So, by using the visibility modifiers you are doing the first step to OOP. Or, well, it may be OOP already, but to me, there's more!

I am using the visibility modifiers correctly and I am not creating a struct, I am creating a class! So it is object oriented!

In fact, creating a struct or a class is not the problem, as in .NET a struct only changes how it is allocated and in some C++ implementations the difference of a struct to a class is only the default visibility. To me real OOP only exists when we use Inheritance and method overriding correctly (or, as some say, when we use the right abstractions).

To illustrate the problem, I worked on some places that used ORM like objects, having one class to represent each database table. They all had the same structure:

  • All the fields that existed in the database had a Get and a Set method;
  • There were some methods like Save, Delete etc.

But they had a little problem, there was not a common base. So, I couldn't create a method like DeleteWithConfirmation who was capable of showing a message and, if it was confirmed, delete the given record.

The problem is, to make such method, I should create it for a Person or for a Product (or for something else) when I should be creating the method for DatabaseRecords in general.

So, the second step to OOP: Create the ancestor. A class like DatabaseRecord with a Delete method will work. Then, both Person and Product could inherit from DatabaseRecord and the method DeleteWithConfirmation could be created, showing the message and, if confirmed, calling the record.Delete() method.

But, how should look the Delete implementation? It is created at the DatabaseRecord, with does not know if it is a Person or Product.

Solution, create it as abstract and force the inheritors to implement (override) it.

Getting another sentence from Wikipedia, there's a topic named "Polymorphism Takes Any Shape" which starts with "Object-oriented programming lets programmers create procedures for objects whose exact type is not known until runtime". That means, for our case, that the DeleteWithConfirmation works for DatabaseRecords, independently if their real types are Persons, Products or anything else.

So, we finished the basic OOP here. We need to use encapsulation (that is, make field private and make they accessible only through methods or properties, even if at the first moment it seems to be too many extra code) and have a class that has at least one virtual method (that we have real reasons to override) to say that we are using OOP. Simply creating a class or simply making our methods virtual without ever expecting to override them means we are not using OOP, even if we have objects.

But if you are not used to see virtual, override or abstract terms, here they are:

  • virtual: A method declared in one class that can have its actual code replaced by a child class;
  • abstract: It's a virtual method that does not have a default implementation, so instead of allowing its implementation to be replaced, it forces subclasses to implement it;
  • override: The act of implementing an abstract method or reimplementing a virtual method. In C# you need to use the override keyword to explicitly tell that you want to implement or reimplement an abstract or virtual method.

Class inheritance and Interface implementation

In C++ there is multiple inheritance, so it doesn't need interfaces. Independent that I don't agree with the reasons to remove multiple inheritance from the .Net, the main differences between an interface and an abstract class are:

  • An abstract class can have a default implementation, but if you inherit from that class, you can't inherit from any other class;
  • Interfaces don't have a default implementation, but your objects are free to implement as many interfaces as you want.

I usually see the affirmation "Interfaces are a contract".

What does that mean? Did I put my signature in anything? Do I need a lawyer?

The answer may be: The interface has the methods that tell you what you should implement (like a contract that asks you to do one or more things) and implementing it is accepting that contract.

Ok... valid, but I still don't like the contract term. But let's return to the topic.

You can create an abstract class without a default implementation. But should you do it?

In my opinion. No. Never do it. In fact that is how you create an interface in normal C++ (in that case it is valid, as normal C++ does not have interfaces) but doing so in .Net is like creating an interface that can't be implemented by classes that have a different ancestor.

So, if you don't need a default implementation, use an interface.

I can go one step further and say: Always create the interface and receive your parameters as the interface then, if you want, you create an abstract class with a default implementation that people can use when they want to implement the interface.

I can repeat all the words, but I don't get it. Why create an interface and an abstract class? Why not the abstract class directly?

Ok... abstraction is great but makes things complicated.

I will ignore some traits of the MarshalByRefObject (which I consider a real error) and the extension methods (that are a pseudo-solution, not a real solution) and I will use the Stream as example.

In the Stream class, we have the Write(byte[] buffer, int offset, int length) method.

We don't have a Write(byte[] buffer) with simply writes the entire buffer. It will be real easy to implement:

C#
public void Write(byte[] buffer)
{
  if (buffer == null)
    throw new ArgumentNullException("buffer");

  Write(buffer, 0, buffer.Length);
}

What that implementation is doing is simply calling the Write that requires an offset (which will be zero, to get the beginning of the array) and length (which will be the full length of the array) so it will write the full buffer array.

With an abstract class I can do that. With an interface I can only declare that there are two different Write methods, but I can't provide the default implementation to one of them.

So, the abstract class can have a Write(byte[] buffer) that is implemented and a Write(byte[] buffer, int offset, int length) which needs to be implemented by the subclasses (like File and MemoryStream).

But now, to make an example on why it should start as an interface, imagine that I want to create an implementation that logs each action being invoked and then redirects to another Stream that does the real action.

The first problem can be that I need a different base class. In that case, the interface let me do it, the abstract class does not.

Also, if the abstract class did not make the Write with one parameter virtual, I can't reimplement it to do the log, so I will only implement the log on the write with 3 parameters.

The result is: the user calls Write(someBuffer) and the log will say that he called write(someBuffer, 0, ...someBuffer length...) when the wanted result was to log Write(someBuffer).

If, on the other hand, we had an interface, with the two Writes, we will have the option to implement each one with a different log, and then redirect to the real class.

But, in most implementations, we will simply use the Write with one parameter calling the write with 3... so, it will be good to create an abstract class that gives the default implementation to those who want it, but we should use the interface at every place where we consume or use the object, so we are free to receive all the implementations that can do any extra work they want.

AOP - Aspect Oriented Programming

I just showed a log example, so I will continue with it. To me the descriptions of AOP are hard to understand. I am not a natural English speaker, I think I read and write well in English, but terms like cross-cutting concerns don't make any sense to me. Is it only me?

Well, what I see of AOP is that it wants to add functionalities to unrelated classes.

For example, when creating my DatabaseRecord.Save I want only to implement the Save method. I don't want to bother about logging what is being done. That will conform better with the Single Responsibility Principle (we will see it soon) and so we will not be writing code for other concerns.

What is a concern? I still don't really get it, but I will say that logging is a concern about tracing what was being done. A validation if the user can do such modification or not is a security concern, but you may simply want to avoid those concerns when writing your class. You simply want to make your class do what it is supposed to do. Every other concern should (logging, security checks etc) should be put later.

As we can't put code to a class that already exists during execution, interfaces give us the possibility of putting another implementation in place, with the additional concern and then redirecting to the original class.

So, the log class can simply log the beginning of the call, call the original method, and then log the result of the call.

With some JIT we can even implement any interface at run-time to do the log, the security check or whatever.

JIT - Just-in-Time (compilation)

JIT means Just-in-Time compilation and actually it is mostly seen as an optimization technique for languages that compile to an intermediate byte-code. The idea is that during the execution (maybe during the load-time, maybe after an interpreted method is used too often) such intermediate byte-code is compiled to the target processor. Both Java and .Net have intermediate languages and do JIT compilation.

But that's not the only case where JIT is used. If, for any reason, we generate code during the execution of an application, compile it and then use it, we will be doing JIT - even if we compile to an intermediate language.

In this case, it usually helps avoiding repetitive code. To see such scenario, let see the log example with interfaces again.

We may have the original classes doing their action, without any log, and then we need extra classes that do the log.

But, to each method that exist in the interface we will need to provide an implementation that looks the same:

  • Logs beginning;
  • Call the original method;
  • Logs the result.

It will be identical, except that the method name and the parameters change. That's the single thing that changes.

So, why not let such repetitive code be implemented to you?

Well, if you create a code generator that does that then you will avoid writing that repetitive code by hand, but you will still need to compile it as part of your application (ahead of time compilation). But, if you generate and compile such code at run-time, then it will be JIT compilation.

Single Responsibility Principle - SRP

I wrote the real meaning and the abbreviation on the opposite order of the other abbreviations because I see this one more often fully written than as an abbreviation, which is the opposite to the others.

But as the AOP topic shown, SRP is something highly related to AOP... or should I say that AOP helps SRP?

I wrote an article about the SRP itself, I received some complaints and now I will put my view here:

Your classes don't need to deal with anything unrelated with their purposes. You, as a programmer, don't need to put anything to your classes (be it programming instructions, attributes or resources) that are not related to what the class need to do.

That means that if you need to put a single: [Serializable] attribute when you write a class to store some data, you are violating the SRP.

Maybe you use a framework that is forcing you to do so (the .Net serialization does that and you may have no choice). But if you have the option, avoid doing that. It will simplify your code and if you are writing libraries used by other people, you will be simplifying their lives.

Some say that putting a [Serializable] attribute does not add responsibility to a class.

But someone simply writes his class, without knowing that it will be serialized, and then he gets a complaint that his class is not serializable when it should be. So, it became a responsibility of the programmer to put that damn "flag". At the same time, we can say that it became the responsibility of the class to say: Hey, I am serializable!

In that SRP article I even received the response that implementing the ISerializable will violate the principle while the attribute does not. But, in my opinion, if the class was created with the idea of being serializable, implementing the interface is part of its responsibility, so the attribute is violating the principle while the interface is not. (I am already seeing someone discussing about it... I hope you understand my idea before complaining).

Open-Closed Principle

To me the Open-Closed Principle is simply Object-Oriented Programming and it is really needed for those who write libraries that may be used by many different users.

The premise is very simple (I will put in my own words): You don't need to write all the classes. But you will need to create extension points so users may create their own classes that work correctly in your environment.

Let's see Windows Forms for example. It has buttons, textboxes, checkboxes... but you can still create your own control and put it into a window. You don't need to modify the Windows Forms libraries themselves to do that.

But as that is normal inheritance, I should say that it is simply a different explanation of OOP.

And, why open-closed?

Open because it is allows expansion (by inheritance), closed because you don't need to change it (and we as users simply can't change it, it is a closed library).

Loose-coupling

This is one of the most difficult ones to explain and to apply, but I also consider it the most importante one.

When we use an interface instead of a base class we already let our code to be loosely-coupled. If the caller of our method has a different class that conforms with the interface, even if it does a different work than I was expecting (it logs, verifies accesses then calls a remote server instead of doing the work directly) we have loose-coupling.

AOP and the Open-Closed Principle already benefit from it. But there is still a little problem. I will start it by the example:

Imagine that you are working with many persons in a big project with too many different classes, concepts and so on.

So, everyone is aware that parameters should be passed as interfaces. There is no method receiving a File (specific class) or Stream (abstract class), all of them receive (IStream) the interface, which can be loggable or have other concerns (respecting AOP and again ignoring the MarshalByRefObject that already does that in a way I disagree).

So, you do your first call:

C#
var color = CreateObject<IColor>(255, 0, 0, 0);

I suppose that if you work with factories already you understand that I am creating a Color, and that it is black.

But then I do:

C#
serializer.Serializer(color);

I am omitting how the serializer was created on purpose.

The problem here is: a Color is not serializable.

  • It does not has the [Serializable] attribute.
  • It does not implement the ISerializable interface.

But I can easily read its ARGB (Alpha=Opacity, 255 = 100 % opaque, R=Red, G=Green and B=Blue) values and write it to a stream, and recreate the same color easily.

This is not a Cross-cutting concern (so it is not an AOP problem) because I don't want to add an extra actions to some method of the Color type, I want a new action (and so a new responsibility) to a class that's not aware on how to serialize itself.

When the serializer was created (let's say assembly X) it did not know about the existence of the Color type (which is in assembly Y), so it can't have specific code to serialize a color and it is also not its responsibility.

When the Color type was created it also did not know about the Serializer and, even if it knew about its existence, it was not its responsibility to be serializable.

The person who used both assemblies created that need, and so he should create the serialization of a Color in its own assembly (let's say assembly Z).

Creating a ColorSerializer is easy. The problem is, objects are passed to a generic serializer to be serialized. We are not creating an specific ColorSerializer to be able to serialize colors.

So, what should we do?

Creating a BaseSerializer abstract class means that we can create serializers for any other type, but if we call a method that will do some code and then serialize an object we should also give the right serializer to it.

Using the ISerializable interface that makes the object capable of serializing itself means we can create a Color subclass that is capable of serializing itself. What if we create such SerializableColor class, but we are still receiving a Color (not a SerializableColor) created somewhere else, which is still not serializable?

Now, if the serializer (at assembly X) allowed to register specific serialization code for different types (using a Dictionary it is very simple to do it), we could initialize the application, register that a Color is serialized by a ColorSerializer and, at any random moment, we can ask a serializer to serialize an object.

Using that dictionary it discovers that the serializer of Color is the ColorSerializer and uses it.

If that value was a string, it could also search on that dictionary for the serializer of strings.

So, that's loose-coupling.

But to do that, the Serializer class should be created with the idea of expansion in mind. It will surely use some interface or abstract class, but it should not expect that the serialized items themselves implement the interface. They are bind at run-time.

So:

  • If you can inherit your Color class to make it serializable, you are following the OOP and the Open-Closed Principle, even the Single Responsibility Principle, but you are not creating a loose-coupled code and you will be stuck when the object is of the base class (Color) instead of being of the child class (SerializableColor);
  • If you can inherit your Serializer to it can know how to serialize Colors you are also following all the same principles, and have the same problem. If you can't control which serializer is created, you will not be able to serialize a color;
  • But if you can create a ColorSerializer and register it as the serializer for colors and the Serializer class is capable of finding the right object serializer by using a dictionary (for example), then you will solve the problem and have loose-coupling.

IoC - Inversion of Control

Inversion of Control is a problematic principle because the term is too generic and usually the texts say one thing and give examples of another thing.

As the name tries to say, the Control is inverted. But, what kind of control are we talking about? And the answer is that it can be two kinds of control.

  • What: In this situation, the control of what exactly is being done is not part of the executing code. This happens, for example, when the code receives a delegate or an interface and it calls the delegate/interface as part of its execution. Surely there is some rule to be followed, but it does not even know if a property set will do a validation, will write that property value to the console or to a text file or what else it can do.
  • When: In this situation we are talking about the delegate or the interface itself. The delegate is given to a method. But the delegate can't control when it will be called or even if it will be called at all.

They are too similar right? In fact, it is enough to create an interface or delegate and we already have inversion of control, right?

Well, that's when things got complicated. Most documents talk about Inversion of Control as only the second case, the when situation, but then they use an example that's like this:

C#
public static void ThisIsNotIoC()
{
  IData data = new Data();
  data.FirstName = "Paulo";
  data.LastName = "Zemek";
}
public static void ThisIsIoC(IData data)
{
  data.FirstName = "Paulo";
  data.LastName = "Zemek";
}

In such sample, the code is always in control of when the FirstName and LastName will be set and the Data class is never in control of when its properties will be set. Yet, the first example is not seen as IoC while the second example is. That's the problem I see in most documents and the truth is: There is Inversion of Control, and it is about what is going to happen when the properties are set (or what the data really is). In the first case, we know it is a Data instance, so if we know what it does, we already know what's going to happen. In the second case, the data object may be of type Data, of type LoggableData or any other type, and we can't control what we are effectively doing by setting such properties.

Factories

I used the example of creating an object like this:

C#
var color = CreateObject<IColor>(255, 0, 0, 0);

We can say that CreateObject is a factory method (it creates objects) and that we are using Inversion of Control too.

The inversion of control here means that we are not in control of the created object (the what from the previous topic).

If we used something like:

C#
var color = new Color(255, 0, 0, 0)

We will be in control. The real purpose of the factories is to reduce direct dependencies between classes, which is already a kind of loosely-coupling and, depending on the implementation, it can be completely loosely-coupled and allow dependency injection.

Dependency Injection

This is another term that I can't agree. I don't know if injection has a different meaning than the one I have in my mind, but when I see Dependency Injection I think we will be adding dependencies. So, the code didn't depend on anything and we put something that it must depend now.

But what happens is that the code already depends on something, which may not be there, and we fill the dependency (so, I will call it Dependency Filling).

Example? Well, on the CreateObject<IColor> case, imagine the the CreateObject method searches for the right type to create in a dictionary. Such dictionary starts empty and, so, the code that depends on IColor will not work until we register (inject) the implementation for IColor.

Something simple as Register(typeof(IColor), typeof(Color)) could solve the problem, and can be done in a different moment of the CreateObject call, like on the application startup. So, such register is going to inject the dependency... or, as I said, fill it, as the dependency is already there.

Library versus Framework

After the initial publication I received a comment in which I was asked to explain the difference between a library and a framework.

Personally, I call any .DLL a library. So, most frameworks start as a library. But one common definition is something like "you call a library, a framework calls you".

To such definition, a library is simply a class or method that you use when you need it. You don't need to follow specific rules to use it. On the other hand, a framework is usually a structure that works on its own, and you need to extend it by implementing some base classes or filling events and, in that case, the framework is going to call your code on the right moments.

I think that such affirmation works most of the time, but I can't really say that I agree with it. I will use as an example my ConversionManagement framework. I call it a framework. If you create specific conversions for it, it is going to call your code. But if you don't, and you simply use it in its original form, the framework will not be calling your code, but I can't say it will become a library. Ok, maybe it is a framework used like a library. But, then, what is a framework? A library that can call your code? Is that really enough?

I can't say I have a direct answer, but to me a framework is usually much more complete than a simple library and you usually (always?) need to follow some rules to use it properly.

Conclusion

I really hope this article helps understanding those principles. There are a lot of names, but in the end, if you can create really loosely coupled applications, you will end-up using all the other principles, even if you don't know their names.

Version History

  • November, 10, 2012. Rewrote IoC and Factories and added Dependency Injection;
  • November, 08, 2012. Corrected a small confusing affirmation and added the library vs framework;
  • November, 07, 2012. Added more details on JIT;
  • October, 25, 2012. Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions

 
QuestionPrefer .NET to .Net Pin
PaperTape15-Mar-16 4:53
professionalPaperTape15-Mar-16 4:53 
AnswerRe: Prefer .NET to .Net Pin
Paulo Zemek16-Mar-16 13:43
mvaPaulo Zemek16-Mar-16 13:43 
Questionstruct / class Pin
John Torjo20-Oct-15 23:36
professionalJohn Torjo20-Oct-15 23:36 
AnswerRe: struct / class Pin
Paulo Zemek21-Oct-15 8:36
mvaPaulo Zemek21-Oct-15 8:36 
GeneralMy vote of 5 Pin
M Rayhan8-Nov-13 0:13
M Rayhan8-Nov-13 0:13 
GeneralRe: My vote of 5 Pin
Paulo Zemek8-Nov-13 1:50
mvaPaulo Zemek8-Nov-13 1:50 
QuestionDefinition of OOP Pin
Bill_Hallahan6-Nov-13 17:31
Bill_Hallahan6-Nov-13 17:31 
AnswerRe: Definition of OOP Pin
Paulo Zemek7-Nov-13 2:39
mvaPaulo Zemek7-Nov-13 2:39 
GeneralRe: Definition of OOP Pin
Bill_Hallahan7-Nov-13 12:26
Bill_Hallahan7-Nov-13 12:26 
GeneralRe: Definition of OOP Pin
Paulo Zemek7-Nov-13 14:05
mvaPaulo Zemek7-Nov-13 14:05 
GeneralRe: Definition of OOP Pin
Bill_Hallahan7-Nov-13 15:10
Bill_Hallahan7-Nov-13 15:10 
GeneralRe: Definition of OOP Pin
Paulo Zemek7-Nov-13 15:26
mvaPaulo Zemek7-Nov-13 15:26 
GeneralRe: Definition of OOP Pin
Bill_Hallahan7-Nov-13 15:40
Bill_Hallahan7-Nov-13 15:40 
GeneralRe: Definition of OOP Pin
Paulo Zemek7-Nov-13 15:45
mvaPaulo Zemek7-Nov-13 15:45 
GeneralRe: Definition of OOP Pin
WuRunZhe7-Nov-13 16:11
WuRunZhe7-Nov-13 16:11 
GeneralRe: Definition of OOP Pin
Bill_Hallahan7-Nov-13 17:29
Bill_Hallahan7-Nov-13 17:29 
GeneralRe: Definition of OOP Pin
Paulo Zemek7-Nov-13 17:47
mvaPaulo Zemek7-Nov-13 17:47 
GeneralMy vote of 3 Pin
Viktor Stolbovoy12-Nov-12 6:31
Viktor Stolbovoy12-Nov-12 6:31 
GeneralYour description of IOC & DI Pin
cbaker@yoshy.globalnet.co.uk12-Nov-12 4:26
cbaker@yoshy.globalnet.co.uk12-Nov-12 4:26 
GeneralRe: Your description of IOC & DI Pin
cbaker@yoshy.globalnet.co.uk13-Nov-12 19:34
cbaker@yoshy.globalnet.co.uk13-Nov-12 19:34 
GeneralRe: Your description of IOC & DI Pin
Paulo Zemek15-Nov-12 2:03
mvaPaulo Zemek15-Nov-12 2:03 
GeneralRe: Your description of IOC & DI Pin
cbaker@yoshy.globalnet.co.uk15-Nov-12 9:46
cbaker@yoshy.globalnet.co.uk15-Nov-12 9:46 
GeneralRe: Your description of IOC & DI Pin
Paulo Zemek16-Nov-12 4:54
mvaPaulo Zemek16-Nov-12 4:54 
GeneralRe: Your description of IOC & DI Pin
Jarmo Muukka18-Nov-13 4:25
Jarmo Muukka18-Nov-13 4:25 
GeneralRe: Your description of IOC & DI Pin
Paulo Zemek18-Nov-13 7:34
mvaPaulo Zemek18-Nov-13 7:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.