PS:本文PDF版在這里(格式更好看一些)。最新的源代碼請在本頁面文末下載,PDF中的鏈接不是最新的。
用C表達面向對象語言的機制——C#版
我一直認為,面向對象語言是對面向過程語言的封裝。如果是這樣,那么就應該能夠用C來模擬C#的代碼風格,寫出面向對象形式的代碼。本文逐步展示了與C#對應的C代碼是如何實現的。
1. 目標
面向對象語言的三大特性(封裝、繼承、多態)中,封裝和繼承的C版寫法需要研究,而多態似乎不關乎新的寫法。
所以本文就展示如何用C來模擬C#的封裝、繼承、虛方法、關鍵字as和interface的寫法。
2. 封裝字段和方法
例如如下的C#代碼,寫了一個典型的類。
class BaseClass
{
public int baseField1 = 0;
public void BaseMethod1()
{
Console.WriteLine("BaseClass.BaseMethod1()");
}
}
其典型的使用方式如下。
// create a BaseClass object
BaseClass pBaseClassObj = new BaseClass();
// use BaseClass object's field
Console.Write("BaseClass obj : pBaseClassObj->baseField1({0})\n", pBaseClassObj.baseField1);
// use BaseClass object's method
pBaseClassObj.BaseMethod1();
如果想用C來實現類似的使用方式,應該如何寫呢?
1) 用struct代替class
用C的struct代替C#的class的字段
typedef struct _BaseClass
{
int baseField1;
} BaseClass;
但是struct沒有“在類型里面寫方法”這種功能。我們用變通的方法來實現。
void BaseMethod1(BaseClass * pThis)
{
printf("BaseClass.BaseMethod1()\n");
}
就是說,每個方法都加上指向這個結構體的指針。
2) 用New[ClassName]代替new
一個class類型都有至少一個構造方法,是專門給new關鍵字用的。所以我們寫一個C版的new方法。
BaseClass * NewBaseClass()
{
// initialize base class
// alloc for space
BaseClass * pResult = (BaseClass * )malloc(sizeof(BaseClass));
// initialize fields
pResult->baseField1 = 0;
// return result
return pResult;
}
C版的New方法用malloc來申請內存空間,這樣就把對象new到了堆上,和面向對象語言的處理方式相同。
其使用方法如下。
// create a BaseClass object
BaseClass * pBaseClassObj = NewBaseClass();
// use BaseClass object's field
printf("BaseClass obj : pBaseClassObj->baseField1(%d)\n", pBaseClassObj->baseField1);
// use BaseClass object's method
BaseMethod1(pBaseClassObj);
與C#版的使用方法異曲同工。
3. 實現繼承
在上文的BaseClass基礎上,來展示如下代碼的實現。
class DerivedClass : BaseClass
{
public int derivedField1 = 0;
public void DerivedMethod1()
{
Console.WriteLine("DerivedClass.DerivedMethod1()");
}
}
3) 用組合代替繼承
仍然用struct來代替class,但DerivedClass繼承了BaseClass這一性質,如何實現呢?C語言雖然沒有“繼承”的概念,但是struct可以“組合”(與C#的組合概念一樣),我們用一個BaseClass的指針來表示“繼承”的概念。

用組合模擬繼承的機制typedef struct _DerivedClass
{
// base type
BaseClass * pBase;
// own fields
int derivedField1;
} DerivedClass;
//DerivedClass * pDerivedClassNULL = NULL;
DerivedClass * NewDerivedClass()
{
// initialize base class
BaseClass * pBase = NewBaseClass();
// alloc for space
DerivedClass * pResult = (DerivedClass * )malloc(sizeof(DerivedClass));
pResult->pBase = pBase;
// initialize fields
pResult->derivedField1 = 0;
// return result
return pResult;
}
void DerivedMethod1(DerivedClass * pThis)
{
printf("DerivedClass.DerivedMethod1()\n");
}
在C 版的構造函數NewDerivedClass()里,首先構造了一個“基類”BaseClass的對象,然后賦值給DerivedClass對象的pBase指針。這里也符合面向對象語言“先調用父類的構造函數,再調用子類的構造函數”的特點。具體地說,是“首先,子類的構造函數調用父類的構造函數;然后,完成對父類構造函數的調用;最后,完成對子類函數的調用”。
子類的典型使用方式是:
// create a DerivedClass object
DerivedClass pDerivedClassObj = new DerivedClass();
// use DerivedClass object's field
Console.Write("DerivedClass obj : pDerivedClassObj->derivedField1({0})\n", pDerivedClassObj.derivedField1);
// use DerivedClass object's base class' field
Console.Write("DerivedClass obj : pDerivedClassObj->baseField1({0})\n", pDerivedClassObj.baseField1);
// use DerivedClass object's method
pDerivedClassObj.DerivedMethod1();
// use DerivedClass object's base class' method
pDerivedClassObj.BaseMethod1();
C版的DerivedClass使用方式,仍然與此類似。
// use DerivedClass object's field
DerivedClass * pDerivedClassObj = NewDerivedClass();
// use DerivedClass object's field
printf("DerivedClass obj : pDerivedClassObj->derivedField1(%d)\n", pDerivedClassObj->derivedField1);
// use DerivedClass object's base class' field
printf("DerivedClass obj : pDerivedClassObj->baseField1(%d)\n", pDerivedClassObj->pBase->baseField1);
// use DerivedClass object's method
DerivedMethod1(pDerivedClassObj);
// use DerivedClass object's base class' method
BaseMethod1(pDerivedClassObj->pBase);
這里可以看到,“子類”DerivedClass能夠調用自身的字段和方法,也能夠借助pBase指針調用父類的字段和方法,完全達到了面向對象語言的要求。
4) 用函數指針代替virtual
繼承還有一個特性,就是虛方法。C語言如何做到呢?答案就是使用函數指針。
為展示清楚,我們重新定義兩個類。
class VirtualClass
{
public virtual void VirtualMethod1()
{
Console.Write("VirtualClass.VirtualMethod1()\n");
}
}
class OverrideClass : VirtualClass
{
public override void VirtualMethod1()
{
Console.Write("OverrideClass.VirtualMethod1()\n");
//base.VirtualMethod1();
}
}
其典型的使用方式如下。
// create a VirtualClass object
VirtualClass virtualClassObj = new VirtualClass();
virtualClassObj.VirtualMethod1();
// create a OverrideClass object
OverrideClass overrideClassObj = new OverrideClass();
overrideClassObj.VirtualMethod1();
// OverrideClass object assigned to VirtualClass object
VirtualClass pVirtualClass = new OverrideClass();
pVirtualClass.VirtualMethod1();
其輸出應該是:
VirtualClass.VirtualMethod1()
OverrideClass.VirtualMethod1()
OverrideClass.VirtualMethod1()
虛函數的關鍵性質,在于父類知道子類的override函數在哪兒,這只能在創建子類的時候,改變父類能夠調用的函數。所以很自然就需要函數指針幫忙。按照上文的方法,在New[ClassName]創建父類的時候,讓父類對象的函數指針指向父類的virtual方法。等父類對象創建完畢,繼續創建子類對象的時候,修改父類對象的函數指針,使其指向子類的override方法。(這就要求父類和virtual方法和子類的override方法的聲明完全相同)
其C版代碼如下。

VirtualClasstypedef struct _VirtualClass
{
void (* pVirtual1)(_VirtualClass * );
} VirtualClass;
void VirtualMethod1_VirtualClass(VirtualClass * pThis);
VirtualClass * NewVirtualClass()
{
// initialize base class
// alloc for space
VirtualClass * pResult = (VirtualClass * )malloc(sizeof(VirtualClass));
// initialize fields
// initialize virtual methods
pResult->pVirtual1 = VirtualMethod1_VirtualClass;
// return result
return pResult;
}
void VirtualMethod1_VirtualClass(VirtualClass * pThis)
{
printf("VirtualClass.VirtualMethod1()\n");
}

OverrideClasstypedef struct _OverrideClass
{
VirtualClass * pBase;
} OverrideClass;
void VirtualMethod1_OverrideClass(VirtualClass * pThis);
OverrideClass * NewOverrideClass()
{
// alloc for space
OverrideClass * pResult = (OverrideClass * )malloc(sizeof(OverrideClass));
// initialize base class
pResult->pBase = NewVirtualClass();
// initialize fields
// initialize virtual methods
pResult->pBase->pVirtual1 = VirtualMethod1_OverrideClass;
// return result
return pResult;
}
void VirtualMethod1_OverrideClass(VirtualClass * pThis)
{
printf("OverrideClass.VirtualMethod1()\n");
}
C版的使用方法如下。
// create a VirtualClass object
VirtualClass * virtualClassObj = NewVirtualClass();
virtualClassObj->pVirtual1(virtualClassObj);
// create a OverrideClass object
OverrideClass * overrideClassObj = NewOverrideClass();
overrideClassObj->pBase->pVirtual1(overrideClassObj->pBase);
// OverrideClass object assigned to VirtualClass object
VirtualClass * pVirtualClass = NewOverrideClass()->pBase;
pVirtualClass->pVirtual1(pVirtualClass);
其使用方式異曲同工,而輸出和C#版是一樣的。
如果基類有多個virtual方法,其聲明相同,就可以使用函數指針數組;有幾種不同聲明的virtual方法,基類就要有幾個函數指針(或數組)指著他們。
4. 用Convert2Type代替as
5) 使用關鍵字as
看如下的例子。
class FullClassBase
{
}
class FullClassDerived : FullClassBase
{
public int fcdField1 = 0;
public void fcdMethod1()
{
Console.Write("FullClassDerived.fcdMethod1()\n");
}
}
其使用方式如下。
FullClassBase baseObj = new FullClassDerived();
FullClassDerived derivedObj = baseObj as FullClassDerived;
if (null != derivedObj)
{
derivedObj.fcdMethod1();
}
上一節展示了在C中,如何用父類的指針指向子類的對象。而這里的C#的關鍵字“as”將父類的指針還原為子類的指針,這是如何實現的?
答案:as可以用一個函數代替。函數名叫做Convert2Type(隨便你喜歡什么名字)。as的本質就是一個函數。
我們要做的是:已知一個對象的指針,已知要轉換出來的目標類的類型,求出該對象指向目標類的指針。若不存在,則返回NULL。
我們需要一些準備工作。
6) 準備類型標識結構
1
|
每個類型都要有一個標識符(用int即可),用以區分不同的類型
|
2
|
基類對象要有一個指向子類對象的指針,由于子類可能有多種,顯然只能用void *類型
|
每一個類型都要記錄父類指針、子類指針、唯一標識符等內容。另外,Convert2Type函數只能有一個聲明,不可能把所有類型的指針都傳給他。為了給他盡可能多的數據,我們需要將類型的父類指針、子類指針、唯一標識符等信息封裝為一個單獨的struct Metadata。具有繼承關系的類型,其Metadata對象也相互指向。這樣,Metadata就包含了描述全部繼承關系的數據。

typedef struct _Metadata Metadatatypedef struct _Metadata
{
void * pThis;
int typeId;
_Metadata * pBaseIdentifier;
_Metadata * pDerivedIdentifier;
LinkNode * pInterfaceList;
} Metadata;
//this class object, type id, info struct of base class
Metadata * NewMetadata(
void *pThis, // this class object
int typeId, // type id
Metadata *pBaseId) // info struct of base class
{
Metadata * result = (Metadata *)malloc(sizeof(Metadata));
result->pThis = pThis;
result->typeId = typeId;
result->pBaseIdentifier = pBaseId;
result->pDerivedIdentifier = NULL;
result->pInterfaceList = NewLinkNode();
if (NULL != pBaseId)
{
pBaseId->pDerivedIdentifier = result;
}
return result;
}
為方便說明Convert2Type的實現原理,我們再定義C版的FullClassBase和FullClassDerived。

FullClassBase in Ctypedef struct _FullClassBase
{
// basic info
Metadata * metaInfo;
// fields
// virtual methods
} FullClassBase;
// type id
static int FullClassBaseTypeId = 4;
// method declarations
// the new method
FullClassBase * NewFullClassBase()
{
// alloc for space
FullClassBase * pResult = (FullClassBase *)malloc(sizeof(FullClassBase));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
FullClassBaseTypeId,
NULL);
// initialize fields
// initialize virtual methods
// return result
return pResult;
}

FullClassDerived in Ctypedef struct _FullClassDerived
{
// basic info
Metadata * metaInfo;
// fields
int fcdField1;
// virtual methods
} FullClassDerived;
// type id
static int FullClassDerivedTypeId = 5;
// method declarations
void fcdMethod1(FullClassDerived * pThis);
// the new method
FullClassDerived * NewFullClassDerived()
{
// initialize base class
FullClassBase * pBase = NewFullClassBase();
// alloc for space
FullClassDerived * pResult = (FullClassDerived * )malloc(sizeof(FullClassDerived));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
FullClassDerivedTypeId,
pBase->metaInfo);
// initialize fields
pResult->fcdField1 = 0;
// initialize virtual methods
// return result
return pResult;
}
void fcdMethod1(FullClassDerived * pThis)
{
printf("FullClassDerived.fcdMethod1()\n");
}
現在,Metadata持有父類、自身、子類的信息,將其作為參數傳入Convert2Type函數,是足夠找到目標類型的。
7) 實現Convert2Type

Convert2Typevoid * Convert2Type(Metadata * pThisMetaInfo, int targetTypeId)
{
if (NULL == pThisMetaInfo || targetTypeId < 0) { return NULL; }
void * result = NULL;
if (pThisMetaInfo->typeId == targetTypeId)
{
result = pThisMetaInfo->pThis;
}
else
{
Metadata * pCurrent = pThisMetaInfo;
while (NULL == result && NULL != pCurrent->pBaseIdentifier)
{
result = Convert2Type(pCurrent->pBaseIdentifier, targetTypeId);
pCurrent = pCurrent->pBaseIdentifier;
}
pCurrent = pThisMetaInfo;
while (NULL == result && NULL != pCurrent->pDerivedIdentifier)
{
result = Convert2Type(pCurrent->pDerivedIdentifier, targetTypeId);
pCurrent = pCurrent->pDerivedIdentifier;
}
}
return result;
}
C版的使用方式如下。
FullClassBase * baseObj = (FullClassBase *)(NewFullClassDerived()->metaInfo->pBaseIdentifier->pThis);
FullClassDerived * derivedObj = (FullClassDerived *)Convert2Type(baseObj->metaInfo, FullClassDerivedTypeId);
if (NULL != derivedObj)
{
fcdMethod1(derivedObj);
}
關鍵字“as”通過Convert2Type函數和一個C語言的強制類型轉換實現了。
5. 用鏈表代替interface
C#中的一類可以實現多個interface,這在C中如何表達?
答案是:首先,interface也用struct代替。Interface只不過是一個特殊的類類型,其內部字段都是函數指針。C#的類實現interface,實際上仍然是繼承了這個interface類型。這和繼承父類類型沒有區別。但一個類可以實現多個interface,這就需要用鏈表來記錄這些interface了。
8) 創建鏈表類型LinkNode

typedef struct _LinkNode LinkNodetypedef struct _LinkNode
{
void * pValue;
_LinkNode * pNext;
} LinkNode;
LinkNode * NewLinkNode()
{
LinkNode * result = (LinkNode *)malloc(sizeof(LinkNode));
result->pValue = NULL;
result->pNext = NULL;
return result;
}
其中的void * pValue;用于保存這個類型實現的接口的Metadata。
9) 為Metadata添加用于記錄interface的鏈表指針

新的Metadatatypedef struct _Metadata
{
void * pThis;
int typeId;
_Metadata * pBaseIdentifier;
_Metadata * pDerivedIdentifier;
LinkNode * pInterfaceList;
} Metadata;
//base class object, this class object, type id, info struct of base class
Metadata * NewMetadata(
void *pThis, // base class object, this class object
int typeId, // type id
Metadata *pBaseId) // info struct of base class
{
Metadata * result = (Metadata *)malloc(sizeof(Metadata));
result->pThis = pThis;
result->typeId = typeId;
result->pBaseIdentifier = pBaseId;
result->pDerivedIdentifier = NULL;
result->pInterfaceList = NewLinkNode();
if (NULL != pBaseId)
{
pBaseId->pDerivedIdentifier = result;
}
return result;
}
10) 修改Convert2Type函數

新的Convert2Type函數void * Convert2Type(Metadata * pThisMetaInfo, int targetTypeId)
{
if (NULL == pThisMetaInfo || targetTypeId < 0) { return NULL; }
void * result = NULL;
if (pThisMetaInfo->typeId == targetTypeId) // this type is target type?
{
result = pThisMetaInfo->pThis;
}
else // some interface type of this object is target type?
{
LinkNode * pCurrent = pThisMetaInfo->pInterfaceList;
while (NULL == result && NULL != pCurrent)
{
void * pValue = pCurrent->pValue;
if (NULL != pValue)
{
Metadata * pInterface = (Metadata *)pValue;
if (pInterface->typeId == targetTypeId)
{
result = pInterface->pThis;
break;
}
}
pCurrent = pCurrent->pNext;
}
}
if (NULL == result) // look into its derived type and base type
{
Metadata * pCurrent = pThisMetaInfo;
while (NULL == result && NULL != pCurrent->pDerivedIdentifier)
{
result = Convert2Type(pCurrent->pDerivedIdentifier, targetTypeId);
pCurrent = pCurrent->pDerivedIdentifier;
}
pCurrent = pThisMetaInfo;
while (NULL == result && NULL != pCurrent->pBaseIdentifier)
{
result = Convert2Type(pCurrent->pBaseIdentifier, targetTypeId);
pCurrent = pCurrent->pBaseIdentifier;
}
}
return result;
}
11) 驗證
下面就來驗證一下。
我們創建一個interface類型,讓FullClassBase實現他。
interface InterfaceClass
{
void Method4InterfaceClass();
}
class FullClassBase : InterfaceClass
{
public void Method4InterfaceClass()
{
Console.Write("FullClassBase.Method4InterfaceClass()\n");
}
}
對應的C代碼如下。
typedef struct _InterfaceClass
{
Metadata * metaInfo;
void (*pInterfaceMethod1)(_InterfaceClass *);
} InterfaceClass;
static int InterfaceClassTypeId = 6;
InterfaceClass * NewInterfaceClass()
{
InterfaceClass * result = (InterfaceClass *)malloc(sizeof(InterfaceClass));
result->metaInfo = NewMetadata(result, InterfaceClassTypeId, NULL);
result->pInterfaceMethod1 = NULL;
return result;
}
typedef struct _FullClassBase
{
// basic info
Metadata * metaInfo;
// fields
// virtual methods
} FullClassBase;
// type id
static int FullClassBaseTypeId = 4;
// method declarations
void Method4InterfaceClass(InterfaceClass * pInterfaceClass);
// the new method
FullClassBase * NewFullClassBase()
{
// alloc for space
FullClassBase * pResult = (FullClassBase *)malloc(sizeof(FullClassBase));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
FullClassBaseTypeId,
NULL);
InterfaceClass * interfacePart = NewInterfaceClass();
interfacePart->metaInfo->pDerivedIdentifier = pResult->metaInfo;
interfacePart->pInterfaceMethod1 = Method4InterfaceClass;
LinkNode * nextNode = NewLinkNode();
nextNode->pValue = interfacePart->metaInfo;
pResult->metaInfo->pInterfaceList->pNext = nextNode;
// initialize fields
// initialize virtual methods
// return result
return pResult;
}
void Method4InterfaceClass(InterfaceClass * pInterfaceClass)
{
printf("FullClassBase.Method4InterfaceClass()\n");
}
C#版的使用方式如下。
FullClassBase obj = new FullClassBase();
obj.Method4InterfaceClass();
InterfaceClass interfaceObj = obj as InterfaceClass;
interfaceObj.Method4InterfaceClass();
對應的C版代碼的使用方式如下。
FullClassBase * obj = NewFullClassBase();
Method4InterfaceClass((InterfaceClass *)Convert2Type(obj->metaInfo, InterfaceClassTypeId));
InterfaceClass * interfaceObj = (InterfaceClass *)Convert2Type(obj->metaInfo, InterfaceClassTypeId);
Method4InterfaceClass(interfaceObj);
12) 關于interface的性質
有了interface以后,實際上就是出現了“多繼承”。
一個接口可以被多個類型繼承,所以C版代碼里,interface里的函數指針類型的第一個參數只能是interface本身。即:一個類型A,實現了interface X里的方法,就意味著這個方法的第一個參數類型變成了X的指針(而不再是A的指針)。
基于這樣的C版設計,在調用接口函數時,一定會發生類型轉換(調用Convert2Type函數)。
所以,如果在interface里的方法,需要用this指針的時候,會發生從X的指針到A的指針的類型轉換。
6. 虛函數與接口與多次繼承
如果接口的方法在基類里標記為virtual,并且在子類和孫類里都override了,會怎么樣?

虛函數與接口與多次繼承 interface VirtualAndInterfaceInterface
{
void InterfaceMethod();
}
class VirtualAndInterfaceBase : VirtualAndInterfaceInterface
{
public virtual void InterfaceMethod()
{
Console.WriteLine("VirtualAndInterfaceBase.InterfaceMethod()");
}
}
class VirtualAndInterfaceDerived : VirtualAndInterfaceBase
{
public override void InterfaceMethod()
{
Console.WriteLine("VirtualAndInterfaceDerived.InterfaceMethod()");
}
}
class VirtualAndInterfaceDerived2 : VirtualAndInterfaceDerived
{
public override void InterfaceMethod()
{
Console.WriteLine("VirtualAndInterfaceDerived2.InterfaceMethod()");
}
}
對于這樣的情況,下面的情形會輸出什么?
VirtualAndInterfaceDerived2 obj = new VirtualAndInterfaceDerived2();
VirtualAndInterfaceBase baseObj = obj;
VirtualAndInterfaceInterface interfaceObj = obj;
obj.InterfaceMethod();
baseObj.InterfaceMethod();
interfaceObj.InterfaceMethod();
答案是三行“VirtualAndInterfaceDerived2.InterfaceMethod()”,即全部調用了最后override的方法。就是說,轉換出來的C代碼中,除了基類的virtual方法的函數指針外,接口對象的函數指針也要修改為指向最后override的函數。
這又帶來一個問題。如果我們定義一個新的接口。
interface VirtualAndInterfaceInterface2
{
void InterfaceMethod();
}
這個新接口和VirtualAndInterfaceInterface所擁有的方法聲明相同。現在讓VirtualAndInterfaceDerived2實現這個接口。
class VirtualAndInterfaceDerived2 : VirtualAndInterfaceDerived, VirtualAndInterfaceInterface2
{
public override void InterfaceMethod()
{
Console.WriteLine("VirtualAndInterfaceDerived2.InterfaceMethod()");
}
}
我們發現無需其他改動即可編譯。
根據之前的結論,在C版代碼中,接口類型的函數指針的第一個參數類型只能是接口類型本身。這也使得class類型的C版類型中,其函數類型的第一個參數只能是接口類型。但是在這個例子里,InterfaceMethod方法同時成為VirtualAndInterfaceInterface和VirtualAndInterfaceInterface2的方法。那么InterfaceMethod的第一個參數類型就無從選擇了。
為解決這個問題,我們重新審視接口的定義。在C版代碼中,InterfaceMethod第一個參數類型,就應該是接口本身,這無法改變。實現了此接口的類類型,應該單獨為InterfaceMethod添加一個聲明和InterfaceMethod相同的函數,以滿足該接口的需要。在類類型里的InterfaceMethod,借助Convert2Type函數,直接調用自己內部的InterfaceMethod函數。
C版的代碼如下。

VirtualAndInterfaceInterfacetypedef struct _VirtualAndInterfaceInterface
{
Metadata * metaInfo;
void (*pInterfaceMethod)(_VirtualAndInterfaceInterface *);
} VirtualAndInterfaceInterface;
static int VirtualAndInterfaceInterfaceTypeId = 8;
VirtualAndInterfaceInterface * NewVirtualAndInterfaceInterface()
{
VirtualAndInterfaceInterface * result = (VirtualAndInterfaceInterface *)malloc(sizeof(VirtualAndInterfaceInterface));
result->metaInfo = NewMetadata(result, VirtualAndInterfaceInterfaceTypeId, NULL);
result->pInterfaceMethod = NULL;
return result;
}

VirtualAndInterfaceInterface2typedef struct _VirtualAndInterfaceInterface2
{
Metadata * metaInfo;
void (*pInterfaceMethod)(_VirtualAndInterfaceInterface2 *);
} VirtualAndInterfaceInterface2;
static int VirtualAndInterfaceInterface2TypeId = 10;
VirtualAndInterfaceInterface2 * NewVirtualAndInterfaceInterface2()
{
VirtualAndInterfaceInterface2 * result = (VirtualAndInterfaceInterface2 *)malloc(sizeof(VirtualAndInterfaceInterface2));
result->metaInfo = NewMetadata(result, VirtualAndInterfaceInterface2TypeId, NULL);
result->pInterfaceMethod = NULL;
return result;
}

VirtualAndInterfaceBasetypedef struct _VirtualAndInterfaceBase
{
// basic info
Metadata * metaInfo;
// fields
// virtual methods
void (* pInterfaceMethod)(_VirtualAndInterfaceBase *);
} VirtualAndInterfaceBase;
// type id
static int VirtualAndInterfaceBaseTypeId = 7;
// method declarations
void InterfaceMethod_VirtualAndInterfaceInterface(VirtualAndInterfaceInterface * pThis);
void InterfaceMethod_VirtualAndInterfaceBase(VirtualAndInterfaceBase * pThis);
// the new method
VirtualAndInterfaceBase * NewVirtualAndInterfaceBase()
{
// initialize base class and interfaces
VirtualAndInterfaceInterface * pBaseVirtualAndInterfaceInterface = NewVirtualAndInterfaceInterface();
// alloc for space
VirtualAndInterfaceBase * pResult = (VirtualAndInterfaceBase *)malloc(sizeof(VirtualAndInterfaceBase));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
VirtualAndInterfaceBaseTypeId,
NULL);
LinkNode * nextNode = NewLinkNode();
nextNode->pValue = pBaseVirtualAndInterfaceInterface->metaInfo;
pResult->metaInfo->pInterfaceList->pNext = nextNode;
pBaseVirtualAndInterfaceInterface->metaInfo->pDerivedIdentifier = pResult->metaInfo;
pBaseVirtualAndInterfaceInterface->pInterfaceMethod = InterfaceMethod_VirtualAndInterfaceInterface;
// initialize fields
// initialize virtual methods
pResult->pInterfaceMethod = InterfaceMethod_VirtualAndInterfaceBase;
// return result
return pResult;
}
void InterfaceMethod_VirtualAndInterfaceInterface(VirtualAndInterfaceInterface * pThis)
{
VirtualAndInterfaceBase * pBase = (VirtualAndInterfaceBase *)Convert2Type(
pThis->metaInfo, VirtualAndInterfaceBaseTypeId);
pBase->pInterfaceMethod(pBase);
}
void InterfaceMethod_VirtualAndInterfaceBase(VirtualAndInterfaceBase * pThis)
{
printf("VirtualAndInterfaceBase.InterfaceMethod()\n");
}

VirtualAndInterfaceDerivedtypedef struct _VirtualAndInterfaceDerived
{
// basic info
Metadata * metaInfo;
// fields
// virtual methods
} VirtualAndInterfaceDerived;
// type id
static int VirtualAndInterfaceDerivedTypeId = 9;
// method declarations
void InterfaceMethod_VirtualAndInterfaceDerived(VirtualAndInterfaceDerived * pThis);
// the new method
VirtualAndInterfaceDerived * NewVirtualAndInterfaceDerived()
{
// initialize base class and interfaces
VirtualAndInterfaceBase * pBase = NewVirtualAndInterfaceBase();
// alloc for space
VirtualAndInterfaceDerived * pResult = (VirtualAndInterfaceDerived *)malloc(sizeof(VirtualAndInterfaceDerived));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
VirtualAndInterfaceDerivedTypeId,
pBase->metaInfo);
// initialize fields
// initialize virtual methods
pBase->pInterfaceMethod = InterfaceMethod_VirtualAndInterfaceDerived;
// return result
return pResult;
}
void InterfaceMethod_VirtualAndInterfaceDerived(VirtualAndInterfaceBase * pThis)
{
printf("VirtualAndInterfaceDerived.InterfaceMethod()\n");
}

VirtualAndInterfaceDerived2typedef struct _VirtualAndInterfaceDerived2
{
// basic info
Metadata * metaInfo;
// fields
// virtual methods
} VirtualAndInterfaceDerived2;
// type id
static int VirtualAndInterfaceDerived2TypeId = 9;
// method declarations
void InterfaceMethod_VirtualAndInterfaceInterface2(VirtualAndInterfaceInterface2 *pThis);
void InterfaceMethod_VirtualAndInterfaceDerived2(VirtualAndInterfaceDerived2 * pThis);
// the new method
VirtualAndInterfaceDerived2 * NewVirtualAndInterfaceDerived2()
{
// initialize base class and interfaces
VirtualAndInterfaceDerived * pBase = NewVirtualAndInterfaceDerived();
VirtualAndInterfaceInterface2 *pBaseVirtualAndInterfaceInterface2 = NewVirtualAndInterfaceInterface2();
// alloc for space
VirtualAndInterfaceDerived2 * pResult = (VirtualAndInterfaceDerived2 *)malloc(sizeof(VirtualAndInterfaceDerived2));
// initialize basic info
pResult->metaInfo = NewMetadata(
pResult,
VirtualAndInterfaceDerived2TypeId,
pBase->metaInfo);
LinkNode * nextNode = NewLinkNode();
nextNode->pValue = pBaseVirtualAndInterfaceInterface2->metaInfo;
pResult->metaInfo->pInterfaceList->pNext = nextNode;
pBaseVirtualAndInterfaceInterface2->metaInfo->pDerivedIdentifier = pResult->metaInfo;
pBaseVirtualAndInterfaceInterface2->pInterfaceMethod = InterfaceMethod_VirtualAndInterfaceInterface2;
// initialize fields
// initialize virtual methods
VirtualAndInterfaceBase * pVirtualAndInterfaceBase = (VirtualAndInterfaceBase *)(pBase->metaInfo->pBaseIdentifier->pThis);
pVirtualAndInterfaceBase->pInterfaceMethod = InterfaceMethod_VirtualAndInterfaceDerived2;
// return result
return pResult;
}
void InterfaceMethod_VirtualAndInterfaceInterface2(VirtualAndInterfaceInterface2 *pThis)
{
VirtualAndInterfaceBase * pBase = (VirtualAndInterfaceBase *)Convert2Type(
pThis->metaInfo, VirtualAndInterfaceBaseTypeId);
pBase->pInterfaceMethod(pBase);
}
void InterfaceMethod_VirtualAndInterfaceDerived2(VirtualAndInterfaceBase * pThis)
{
printf("VirtualAndInterfaceDerived2.InterfaceMethod()\n");
}
簡單來說,類型C實現了接口IC,那么,C就要為IC的各個函數分別創建聲明完全相同的函數,這些函數通過Convert2Type得到需要的類型指針,再調用C內部的函數。
7. public、protected和private
這三個關鍵字用C是無法實現的,他們是面向對象語言在編譯器進行語義分析時進行處理的。如果不符合規定(例如類型外調用了private的字段),編譯器就報錯,不給你生成代碼。僅此而已。
8. 結論
我們規定:
CA表示基類,CA實現的接口為ICA1,ICA2,。。。
CB繼承自CA,CB實現的接口為ICB1,ICB2,。。。
CC繼承自CC,CC實現的接口為ICC1,ICC2,。。。
某類型X包含的字段為XF1,XF2,。。。
某類型X包含的方法為XM1,XM2,。。。
那么,可以用如下規則將C#代碼翻譯為C代碼。
類型X用struct代替,X中的字段用相應的字段代替;X中的方法用以X的指針為第一個參數的函數代替。
為X添加一個Metadata結構體的指針,用于記錄X的實例的信息:基類實例、子類實例、接口實例(鏈表)、類型編號、實例本身。
為Metadata編寫Convert2Type函數,返回給定實例關聯的目標類型的實例。
為X分配一個唯一的整數作為類型編號。
為X編寫NewX函數,返回X的實例。
CB的NewCB函數,
先創建CA的實例,
然后創建ICB1、ICB2。。。的實例,
然后創建CB的實例,
然后修改CA實例的子類實例指針(指向CB實例),
然后修改ICB1、ICB2。。。的函數指針(指向CB的函數),
然后修改CA中virtual方法的函數指針(指向CB的函數),若此virtual方法也是ICAn的方法,也要修改ICAn的函數指針(指向CB的函數)。
最后強調一下容易混淆的地方。
CA中的virtual方法,在CB中可以override掉,然后還可以繼續在CC中override掉。(而不是不可以繼續override)其函數指針指向最后override的方法。
CA中的virtual方法,如果恰好也是ICA1中的方法,則ICA1中的函數指針也要指向最后override的方法。(而不是CA中的virtual方法)
2016-05-15

感想
至此,本文展示了面向對象語言中的class、new、virtual/override、as、interface等關鍵字的實現機制,展示了將C#翻譯為C的方法。
很早就在想,面向對象語言到底是如何實現的。封裝還簡單,用struct代替class即可。繼承的虛函數特性,只聽過是通過“晚綁定”實現的,然后就找不到其他資料了。這幾天趁國慶假期好好想了想,邊想邊做,用C實現了面向對象的語言特性,也證實了“面向對象語言是對面向過程語言的封裝”這句話。
現在有了把面向對象語言的代碼翻譯為面向過程語言的代碼的途徑。這讓我開始反思,為什么要把C封裝為面向對象語言?面向對象語言是如何從無到有的?最開始的那個人是怎么設計出這樣一套機制的?他之前沒有面向對象的任何概念,他的思路是什么?
最后貼上自己總結的一段話。
機器語言(01串)是對數字電路的計算和控制邏輯的封裝,人可以用打孔紙帶來控制計算機。匯編語言(指令代碼)是對機器語言的封裝,人可以用易于理解、記憶和維護的名稱來(間接)寫機器語言。面向過程語言(例如C)是對匯編語言的封裝,人可以用模塊化的設計思路編寫代碼。面向對象語言(例如C#)是對面向過程語言的封裝,人可以用模擬現實世界的思路編寫代碼。
點此下載源代碼。