"Abstract," can be both noun, adjective, and verb, in English; in general OOP context we can say that 'Abstract Classes or Methods express virtual (or meta-) information, information which may be used (is required to be used) by other Classes, or Methods that inherit from them.
"Abstraction" can mean both a principle of OOP design (noun), and refer to the process (verb) of creating abstract entities in design, and code.
To bring this "down to earth:" in C# .NET, the 'Abstract keyword signifies that a Class is an entity which:
0. is intended to be used as a "provider" of both/either template definitions (abstract members) and implementations of "real" Methods (non-abstract) to Classes that inherit from it.
if everything in an Abstract class is declared 'abstract: then you can say that that Class is equivalent to a Virtual Class. "Real" methods that the Abstract Class implements can be considered as providing "default" behaviors to inheritors.
And that other Class Members declared 'abstract have no "implementation."
1. it can not be instantiated with 'new. so, don't define a public constructor.
2. it cannot be sealed.
3. it cannot have any members (Properties, Fields, Methods) declared 'private
4. it doesn't support multiple inheritance (cannot inherit from Class or Interface)
5. 'abstract Methods cannot declare bodies
So, what does using an 'Abstract Class do for you ?
1. let's you combine some features of
a. Interface ... inheritors from non-abstract Classes must implement each Class member declared as 'abstract
... and ...
b. a "regular" Class that provide build in implementations that the inheriting Class can use, or over-ride.
2. extendable in a way that does not "break" Classes that inherit it.
While this is a short (and dense, and advanced) small article, I think reading this may give you an idea of the power (extendability) possible using the 'abstract/'override synergy to regulate the behavior of Classes: [
^].
Here's a somewhat fanciful example of usage of an Abstract Class:
using System;
namespace AbstractClassNameSpace
{
interface someInterface
{
int someInt { set; get; }
string someString { set; get; }
}
interface someOtherInterface
{
int someInt { set; get; }
string someString { set; get; }
double someBigNumber { set; get; }
}
public abstract class AbstractSandBox : someInterface, someOtherInterface
{
public abstract int someInt { get; set; }
public abstract string someString { get; set; }
public double someBigNumber { get; set; }
public double hypotenuse(double sidea, double sideb )
{
return Math.Sqrt((sidea*sidea) + (sideb*sideb));
}
public abstract string someWeirdStringStuff(string a, string b);
}
public abstract class SomeInheritingClass: AbstractSandBox
{
public override int someInt { get; set; }
public override string someString { get; set; }
public new double hypotenuse(double sidea, double sideb)
{
double hyp = base.hypotenuse(sidea, sideb);
return hyp / Math.E * Math.PI / Math.Log10(hyp);
}
public abstract override string someWeirdStringStuff(string a, string b);
}
public class GrandchildofAbstractClass : SomeInheritingClass
{
public void AccessParentClass()
{
Console.WriteLine(this.hypotenuse(3.0, 4.0));
Console.WriteLine(this.someWeirdStringStuff("whoops", "adaisy"));
}
public void UseAbstractClassSandBox(string a, string b)
{
Console.WriteLine(this.someWeirdStringStuff(a, b));
}
}
}
If you are "up for it," I suggest you paste this code into a Project in Visual Studio, then add break-points in the code for the GrandchildofAbstractClass in both the
AccessParentClass and UseAbstractClassSandBox Methods.
Then, add your own code to create an instance of the 'GrandchildofAbstractClass, and try calling those methods, passing values as required.
Single-step through the code with F11 and watch the flow-of-control.