In the modern high level languages, the machine level details are hidden from the user, so in order to work with CPU cache, memory, network adapters, learning C programming is a must.
C is a general-purpose programming language and can efficiently work on enterprise applications, games, graphics, and applications requiring calculations, etc. C language has a rich library which provides a number of built-in functions. It also offers dynamic memory allocation.
There is no need to learn C before learning C++. They are different languages. It is a common misconception that C++ is in some way dependent on C and not a fully specified language on its own. Just because C++ shares a lot of the same syntax and a lot of the same semantics, does not mean you need to learn C first.
I've never missed it once, not ever. Yes, it [MI] gets complicated, and yes, interfaces do a similar job in many ways - but that isn't the biggest point: in the general sense, it simply isn't needed most of the time. Even single inheritance is overused in many cases.
Prefer aggregation over inheritance!
class foo : bar, baz
is often better handled with
class foo : Ibarrable, Ibazzable
{
...
public Bar TheBar{ set }
public Baz TheBaz{ set }
public void BarFunction()
{
TheBar.doSomething();
}
public Thing BazFunction( object param )
{
return TheBaz.doSomethingComplex(param);
}
}
This way you can swap in and out different implementations of IBarrable and IBazzable to create multiple versions of the App without having to write yet another class.
Dependency injection can help with this a lot.
One of the issues with dealing with multiple inheritance is the distinction between interface inheritance and implementation inheritance.
C# already has a clean implementation of interface inheritance (including choice of implicit or explicit implementations) by using pure interfaces.
If you look at C++, for each class you specify after the colon in the class
declaration, the kind of inheritance you get is determined by the access modifier (private
, protected
, or public
). With public
inheritance, you get the full messiness of multiple inheritance—multiple interfaces are mixed with multiple implementations. With private
inheritance, you just get implementation. An object of "class Foo : private Bar
" can never get passed to a function that expects a Bar
because it's as if the Foo
class really just has a private Bar
field and an automatically-implemented delegation pattern.
Pure multiple implementation inheritance (which is really just automatic delegation) doesn't present any problems and would be awesome to have in C#.
As for multiple interface inheritance from classes, there are many different possible designs for implementing the feature. Every language that has multiple inheritance has its own rules as to what happens when a method is called with the same name in multiple base classes. Some languages, like Common Lisp (particularly the CLOS object system), and Python, have a meta-object protocol where you can specify the base class precedence.
Here's one possibility:
abstract class Gun
{
public void Shoot(object target) {}
public void Shoot() {}
public abstract void Reload();
public void Cock() { Console.Write("Gun cocked."); }
}
class Camera
{
public void Shoot(object subject) {}
public virtual void Reload() {}
public virtual void Focus() {}
}
//this is great for taking pictures of targets!
class PhotoPistol : Gun, Camera
{
public override void Reload() { Console.Write("Gun reloaded."); }
public override void Camera.Reload() { Console.Write("Camera reloaded."); }
public override void Focus() {}
}
var pp = new PhotoPistol();
Gun gun = pp;
Camera camera = pp;
pp.Shoot(); //Gun.Shoot()
pp.Reload(); //writes "Gun reloaded"
camera.Reload(); //writes "Camera reloaded"
pp.Cock(); //writes "Gun cocked."
camera.Cock(); //error: Camera.Cock() not found
((PhotoPistol) camera).Cock(); //writes "Gun cocked."
camera.Shoot(); //error: Camera.Shoot() not found
((PhotoPistol) camera).Shoot();//Gun.Shoot()
pp.Shoot(target); //Gun.Shoot(target)
camera.Shoot(target); //Camera.Shoot(target)
In this case, only the first listed class's implementation is implicitly inherited in the case of a conflict. The class for other base types must be explicitly specified to get at their implementations. To make it more idiot-proof, the compiler can disallow implicit inheritance in the case of a conflict (conflicting methods would always require a cast).
Also, you can implement multiple inheritance in C# today with implicit conversion operators:
public class PhotoPistol : Gun /* ,Camera */
{
PhotoPistolCamera camera;
public PhotoPistol() {
camera = new PhotoPistolCamera();
}
public void Focus() { camera.Focus(); }
class PhotoPistolCamera : Camera
{
public override Focus() { }
}
public static Camera implicit operator(PhotoPistol p)
{
return p.camera;
}
}
It's not perfect, though, as it's not supported by the is
and as
operators, and System.Type.IsSubClassOf()
.
Here is a very useful case for multiple inheritance that I run into all of the time.
As a toolkit vendor, I cannot change published API's or I will break backwards compatibility. One thing that results from that is that I cannot ever add to an interface once I have released it because it would break compilation for anyone implementing it -- the only option is to extend the interface.
This is fine for existing customers, but new ones would see this hierarchy as needlessly complex, and if I were designing it from the beginning, I would not opt to implement it this way -- I have to, or else I will lose backwards compatibility. If the interface is internal, then I just add to it and fix the implementors.
In many cases, the new method to the interface has an obvious and small default implementation, but I cannot provide it.
I would prefer to use abstract classes and then when I have to add a method, add a virtual one with a default implementation, and sometimes we do this.
The issue, of course, is if this class would likely be mixed in to something that is already extending something -- then we have no choice but to use an interface and deal with extension interfaces.
If we think we have this problem in a big way, we opt for a rich event model instead -- which I think is probably the right answer in C#, but not every problem is solved this way -- sometimes you want a simple public interface, and a richer one for extenders.
C# supports single inheritance, interfaces and extension methods. Between them, they provide just about everything that multiple inheritance provides, without the headaches that multiple inheritance brings.
Multiple inheritance isn't supported by the CLR in any way I'm aware of, so I doubt it could be supported in an efficient way as it is in C++ (or Eiffel, which may do it better given that the language is specifically designed for MI).
A nice alternative to Multiple Inheritance is called Traits. It allows you to mix together various units of behavior into a single class. A compiler can support traits as a compile-time extension to the single-inheritance type system. You simply declare that class X includes traits A, B, and C, and the compiler puts the traits you ask for together to form the implementation of X.
For example, suppose you are trying to implement IList(of T). If you look at different implementations of IList(of T), they often share some of the exact same code. That's were traits come in. You just declare a trait with the common code in it and you can use that common code in any implementation of IList(of T) -- even if the implementation already has some other base class. Here's what the syntax might look like:
/// This trait declares default methods of IList<T>
public trait DefaultListMethods<T> : IList<T>
{
// Methods without bodies must be implemented by another
// trait or by the class
public void Insert(int index, T item);
public void RemoveAt(int index);
public T this[int index] { get; set; }
public int Count { get; }
public int IndexOf(T item)
{
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < Count; i++)
if (comparer.Equals(this[i], item))
return i;
return -1;
}
public void Add(T item)
{
Insert(Count, item);
}
public void Clear()
{ // Note: the class would be allowed to override the trait
// with a better implementation, or select an
// implementation from a different trait.
for (int i = Count - 1; i >= 0; i--)
RemoveAt(i);
}
public bool Contains(T item)
{
return IndexOf(item) != -1;
}
public void CopyTo(T[] array, int arrayIndex)
{
foreach (T item in this)
array[arrayIndex++] = item;
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
int i = IndexOf(item);
if (i == -1)
return false;
RemoveAt(i);
return true;
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < Count; i++)
yield return this[i];
}
}
And you use the trait like this:
class MyList<T> : MyBaseClass, DefaultListMethods<T>
{
public void Insert(int index, T item) { ... }
public void RemoveAt(int index) { ... }
public T this[int index] {
get { ... }
set { ... }
}
public int Count {
get { ... }
}
}
Of course, I'm just scratching the surface here. For a more complete description, see the paper Traits: Composable Units of Behavior (PDF).
The Rust language (from Mozilla) has implemented Traits in an interesting way: they noticed that traits are similar to default interface implementations, so they unified interfaces and traits into a single feature (which they call traits). The main difference between traits and default interface implementations (which Java now has) is that traits can contain private or protected methods, unlike traditional interface methods that must be public. If traits and interfaces are not unified into a single feature, then another difference is that you can have a reference to an interface, but you can't have a reference to a trait; a trait is not itself a type.
I actually miss multiple inheritance for one specific reason... the dispose pattern.
EVERY time that I need to implement the dispose pattern, I say to myself: "I wish I could just derive from a class that implements the dispose pattern with a few virtual overrides." I copy and paste the same boiler-plate code into every class that implements IDispose and I hate it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With