共用方式為


建立使用者定義型別 - 編碼

適用於:SQL Server

撰寫使用者定義類型 (UDT) 定義程式代碼時,您必須實作各種功能,視您要實作 UDT 作為類別或結構,以及您選擇的格式和串行化選項而定。

本節中的範例說明實作 Point UDT 做為結構(或 Visual Basic 中的 結構)。 Point UDT 是由 X 和 Y 座標所組成,實作為屬性程式。

定義 UDT 時,需要下列命名空間:

Imports System  
Imports System.Data.SqlTypes  
Imports Microsoft.SqlServer.Server  
using System;  
using System.Data.SqlTypes;  
using Microsoft.SqlServer.Server;  

Microsoft.SqlServer.Server 命名空間包含 UDT 各種屬性所需的物件,而 System.Data.SqlTypes 命名空間包含代表元件可用 SQL Server 原生數據類型的類別。 當然,您的元件可能需要額外的命名空間才能正確運作。 Point UDT 也會使用 System.Text 命名空間處理字串。

注意

不支援使用 /clr:pure 編譯的 Visual C++ 資料庫物件,例如 UDT。

指定屬性

屬性會決定如何使用串行化來建構 UDT 的儲存表示法,以及依值將 UDT 傳輸至用戶端。

需要 Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute串行化屬性是選擇性的。 您也可以指定 Microsoft.SqlServer.Server.SqlFacetAttribute ,以提供 UDT 傳回類型的相關信息。 如需詳細資訊,請參閱 CLR 常式的自訂屬性

點 UDT 屬性

Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute 會將 Point UDT 的儲存格式設定為 NativeIsByteOrdered 設定為 true,這可確保 SQL Server 中的比較結果與 Managed 程式代碼中發生的相同比較相同。 UDT 會實作 System.Data.SqlTypes.INullable 介面,讓 UDT Null 感知。

下列代碼段顯示 Point UDT 的屬性

<Serializable(), SqlUserDefinedTypeAttribute(Format.Native, _  
  IsByteOrdered:=True)> _  
  Public Structure Point  
    Implements INullable  
[Serializable]  
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native,  
  IsByteOrdered=true)]  
public struct Point : INullable  
{  

實作 Nullability

除了正確指定元件的屬性之外,UDT 也必須支援可為 Null。 載入 SQL Server 的 UDT 會感知 Null,但為了讓 UDT 辨識 Null 值,UDT 必須實 作 System.Data.SqlTypes.INullable 介面。

您必須建立名為 IsNull的屬性,才能從CLR程式碼中判斷值是否為 Null。 當 SQL Server 找到 UDT 的 Null 實例時,會使用一般 Null 處理方法來保存 UDT。 如果不需要,伺服器不會浪費時間串行化或還原串行化 UDT,也不會浪費空間來儲存 Null UDT。 每次從 CLR 引進 UDT 時,都會執行 Null 檢查,這表示使用 Transact-SQL IS NULL 建構來檢查 Null UDT 應該一律正常運作。 伺服器也會使用IsNull屬性來測試實例是否為 Null。 一旦伺服器判斷 UDT 為 Null,就可以使用其原生 Null 處理。

IsNullget() 方法絕不是特殊案例。 如果 Point 變數@pNull,則@p.IsNull 預設會評估為 “NULL”,而不是 “1”。 這是因為 IsNull get() 方法的 SqlMethod(OnNullCall) 屬性預設為 false。 因為物件是 Null,因此當要求 屬性時,不會還原串行化物件,因此不會呼叫 方法,而且會傳回預設值 「NULL」。

範例

在下列範例中 is_Null ,變數是私用的,並保留 UDT 實例的 Null 狀態。 您的程式代碼必須維護 is_Null適當的值。 UDT 也必須具有名為 Null 的靜態屬性,以傳回 UDT 的 Null 值實例。 這可讓 UDT 傳回 Null 值,如果實例確實在資料庫中為 Null。

Private is_Null As Boolean  
  
Public ReadOnly Property IsNull() As Boolean _  
   Implements INullable.IsNull  
    Get  
        Return (is_Null)  
    End Get  
End Property  
  
Public Shared ReadOnly Property Null() As Point  
    Get  
        Dim pt As New Point  
        pt.is_Null = True  
        Return (pt)  
    End Get  
End Property  
private bool is_Null;  
  
public bool IsNull  
{  
    get  
    {  
        return (is_Null);  
    }  
}  
  
public static Point Null  
{  
    get  
    {  
        Point pt = new Point();  
        pt.is_Null = true;  
        return pt;  
    }  
}  

IS NULL 與 IsNull

請考慮包含架構 Points(id int,location Point),其中 Point 是 CLR UDT 的數據表,以及下列查詢:

--Query 1  
SELECT ID  
FROM Points  
WHERE NOT (location IS NULL) -- Or, WHERE location IS NOT NULL;  
--Query 2:  
SELECT ID  
FROM Points  
WHERE location.IsNull = 0;  

這兩個查詢都會傳回具有非 Null 位置的點標識碼。 在查詢 1 中,會使用一般 Null 處理,而且不需要還原串行化 UDT。 另一方面,查詢 2 必須還原串行化每個非 Null 物件,並呼叫 CLR 以取得 IsNull 屬性的值。 顯然,使用 IS NULL 會表現出更好的效能,而且不應該有理由從 Transact-SQL 程式代碼讀取 UDT 的 IsNull 屬性。

那麼,IsNull 屬性的使用為何? 首先,需要從 CLR 程式代碼中判斷值是否為 Null 。 其次,伺服器需要一種方式來測試實例是否為 Null,因此伺服器會使用這個屬性。 判斷其為 Null 之後,就可以使用其原生 Null 處理來處理它。

實作 Parse 方法

Parse ToString 方法允許從 UDT 的字串表示來回轉換。 Parse 方法可讓字串轉換成 UDT。 它必須宣告為靜態的 (或在 Visual Basic 中為 Shared),並採用 System.Data.SqlTypes.SqlString 類型的參數。

下列程式代碼會實作Point UDT 的 Parse 方法,以分隔 X 和 Y 座標。 Parse 方法具有 System.Data.SqlTypes.SqlString 類型的單一自變數,並假設 X 和 Y 值是以逗號分隔的字串來提供。 將 Microsoft.SqlServer.Server.SqlMethodAttribute.OnNullCall 屬性設定為 false防止從 Point 的 Null 實例呼叫 Parse 方法。

<SqlMethod(OnNullCall:=False)> _  
Public Shared Function Parse(ByVal s As SqlString) As Point  
    If s.IsNull Then  
        Return Null  
    End If  
  
    ' Parse input string here to separate out points.  
    Dim pt As New Point()  
    Dim xy() As String = s.Value.Split(",".ToCharArray())  
    pt.X = Int32.Parse(xy(0))  
    pt.Y = Int32.Parse(xy(1))  
    Return pt  
End Function  
[SqlMethod(OnNullCall = false)]  
public static Point Parse(SqlString s)  
{  
    if (s.IsNull)  
        return Null;  
  
    // Parse input string to separate out points.  
    Point pt = new Point();  
    string[] xy = s.Value.Split(",".ToCharArray());  
    pt.X = Int32.Parse(xy[0]);  
    pt.Y = Int32.Parse(xy[1]);  
    return pt;  
}  

實作 ToString 方法

ToString 方法會將 Point UDT 轉換成字串值。 在此情況下,Point 類型的 Null 實例會傳回字串 「NULL」。。 ToString 方法會使用 System.Text.StringBuilder 來傳回由 X 和 Y 座標值組成的逗號分隔 System.String,以反轉 Parse 方法。 由於 InvokeIfReceiverIsNull 預設為 false,因此不需要檢查 PointNull 實例。

Private _x As Int32  
Private _y As Int32  
  
Public Overrides Function ToString() As String  
    If Me.IsNull Then  
        Return "NULL"  
    Else  
        Dim builder As StringBuilder = New StringBuilder  
        builder.Append(_x)  
        builder.Append(",")  
        builder.Append(_y)  
        Return builder.ToString  
    End If  
End Function  
private Int32 _x;  
private Int32 _y;  
  
public override string ToString()  
{  
    if (this.IsNull)  
        return "NULL";  
    else  
    {  
        StringBuilder builder = new StringBuilder();  
        builder.Append(_x);  
        builder.Append(",");  
        builder.Append(_y);  
        return builder.ToString();  
    }  
}  

公開UDT屬性

Point UDT 會公開 X 和 Y 座標,這些座標會實作為 System.Int32 類型的公用讀寫屬性。

Public Property X() As Int32  
    Get  
        Return (Me._x)  
    End Get  
  
    Set(ByVal Value As Int32)  
        _x = Value  
    End Set  
End Property  
  
Public Property Y() As Int32  
    Get  
        Return (Me._y)  
    End Get  
  
    Set(ByVal Value As Int32)  
        _y = Value  
    End Set  
End Property  
public Int32 X  
{  
    get  
    {  
        return this._x;  
    }  
    set   
    {  
        _x = value;  
    }  
}  
  
public Int32 Y  
{  
    get  
    {  
        return this._y;  
    }  
    set  
    {  
        _y = value;  
    }  
}  

驗證 UDT 值

使用UDT數據時,SQL Server 資料庫引擎 會自動將二進位值轉換成 UDT 值。 此轉換程式牽涉到檢查值是否適合型別的串行化格式,並確保可以正確還原串行化值。 這可確保值可以轉換成二進位格式。 如果是位元組排序的UDT,這也可確保產生的二進位值符合原始二進位值。 這可防止無效的值保存在資料庫中。 在某些情況下,這種檢查層級可能不足。 當UDT值必須位於預期的定義域或範圍時,可能需要額外的驗證。 例如,實作日期的 UDT 可能需要日值是屬於特定有效值範圍的正數。

Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.ValidationMethodName 属性的 Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute 可讓您提供當數據指派給 UDT 或轉換成 UDT 時,伺服器執行的驗證方法名稱。 在 bcp 公用程式、BULK INSERT、DBCC CHECKDB、DBCC CHECKFILEGROUP、DBCC CHECKTABLE、分散式查詢和表格式數據流 (TDS) 遠端過程調用 (RPC) 作業執行期間,也會呼叫 ValidationMethodName 。 ValidationMethodName預設值為 null,表示沒有驗證方法。

範例

下列代碼段顯示 Point 類別的宣告,指定 ValidatePointValidationMethodName

<Serializable(), SqlUserDefinedTypeAttribute(Format.Native, _  
  IsByteOrdered:=True, _  
  ValidationMethodName:="ValidatePoint")> _  
  Public Structure Point  
[Serializable]  
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native,  
  IsByteOrdered=true,   
  ValidationMethodName = "ValidatePoint")]  
public struct Point : INullable  
{  

如果指定驗證方法,它必須具有看起來像下列代碼段的簽章。

Private Function ValidationFunction() As Boolean  
    If (validation logic here) Then  
        Return True  
    Else  
        Return False  
    End If  
End Function  
private bool ValidationFunction()  
{  
    if (validation logic here)  
    {  
        return true;  
    }  
    else  
    {  
        return false;  
    }  
}  

驗證方法可以有任何範圍,如果值有效,則應該傳回 true否則傳回 false。 如果方法傳 回 false 或擲回例外狀況,則會將值視為無效且引發錯誤。

在下列範例中,程式代碼只允許零或大於 X 和 Y 座標的值。

Private Function ValidatePoint() As Boolean  
    If (_x >= 0) And (_y >= 0) Then  
        Return True  
    Else  
        Return False  
    End If  
End Function  
private bool ValidatePoint()  
{  
    if ((_x >= 0) && (_y >= 0))  
    {  
        return true;  
    }  
    else  
    {  
        return false;  
    }  
}  

驗證方法限制

當伺服器執行轉換時,伺服器會呼叫驗證方法,而不是藉由設定個別屬性或使用 Transact-SQL INSERT 語句插入數據來插入數據時。

如果您想要在所有情況下執行驗證方法,您必須從屬性 setter 和 Parse 方法明確呼叫驗證方法。 這不是必要條件,在某些情況下甚至可能不想要。

剖析驗證範例

若要確保在 Point 類別中叫用 ValidatePoint 方法,您必須從 Parse 方法呼叫它,以及從設定 X 和 Y 坐標值的屬性程式呼叫它。 下列代碼段示範如何從 Parse 函式呼叫 ValidatePoint 驗證方法。

<SqlMethod(OnNullCall:=False)> _  
Public Shared Function Parse(ByVal s As SqlString) As Point  
    If s.IsNull Then  
        Return Null  
    End If  
  
    ' Parse input string here to separate out points.  
    Dim pt As New Point()  
    Dim xy() As String = s.Value.Split(",".ToCharArray())  
    pt.X = Int32.Parse(xy(0))  
    pt.Y = Int32.Parse(xy(1))  
  
    ' Call ValidatePoint to enforce validation  
    ' for string conversions.  
    If Not pt.ValidatePoint() Then  
        Throw New ArgumentException("Invalid XY coordinate values.")  
    End If  
    Return pt  
End Function  
[SqlMethod(OnNullCall = false)]  
public static Point Parse(SqlString s)  
{  
    if (s.IsNull)  
        return Null;  
  
    // Parse input string to separate out points.  
    Point pt = new Point();  
    string[] xy = s.Value.Split(",".ToCharArray());  
    pt.X = Int32.Parse(xy[0]);  
    pt.Y = Int32.Parse(xy[1]);  
  
    // Call ValidatePoint to enforce validation  
    // for string conversions.  
    if (!pt.ValidatePoint())   
        throw new ArgumentException("Invalid XY coordinate values.");  
    return pt;  
}  

屬性驗證範例

下列代碼段示範如何從設定 X 和 Y 座標的屬性程式呼叫 ValidatePoint 驗證方法。

Public Property X() As Int32  
    Get  
        Return (Me._x)  
    End Get  
  
    Set(ByVal Value As Int32)  
        Dim temp As Int32 = _x  
        _x = Value  
        If Not ValidatePoint() Then  
            _x = temp  
            Throw New ArgumentException("Invalid X coordinate value.")  
        End If  
    End Set  
End Property  
  
Public Property Y() As Int32  
    Get  
        Return (Me._y)  
    End Get  
  
    Set(ByVal Value As Int32)  
        Dim temp As Int32 = _y  
        _y = Value  
        If Not ValidatePoint() Then  
            _y = temp  
            Throw New ArgumentException("Invalid Y coordinate value.")  
        End If  
    End Set  
End Property  
    public Int32 X  
{  
    get  
    {  
        return this._x;  
    }  
    // Call ValidatePoint to ensure valid range of Point values.  
    set   
    {  
        Int32 temp = _x;  
        _x = value;  
        if (!ValidatePoint())  
        {  
            _x = temp;  
            throw new ArgumentException("Invalid X coordinate value.");  
        }  
    }  
}  
  
public Int32 Y  
{  
    get  
    {  
        return this._y;  
    }  
    set  
    {  
        Int32 temp = _y;  
        _y = value;  
        if (!ValidatePoint())  
        {  
            _y = temp;  
            throw new ArgumentException("Invalid Y coordinate value.");  
        }  
    }  
}  

撰寫 UDT 方法程式代碼

撰寫 UDT 方法程式代碼時,請考慮所使用的演算法是否可能會隨著時間而變更。 如果是,您可能想要考慮為 UDT 所使用的方法建立個別類別。 如果演算法變更,您可以使用新的程式碼重新編譯 類別,並將元件載入 SQL Server,而不會影響 UDT。 在許多情況下,UDT 可以使用 Transact-SQL ALTER ASSEMBLY 語句重載,但這可能會導致現有數據發生問題。 例如,AdventureWorks 範例資料庫隨附的 Currency UDT 會使用 ConvertCurrency 函式來轉換貨幣值,這會在個別類別中實作。 轉換演算法未來可能會以無法預測的方式變更,或可能需要新功能。 在規劃未來變更時,將 ConvertCurrency 函式與 Currency UDT 實作分開,可提供更大的彈性。

範例

Point 類別包含三個計算距離的簡單方法:DistanceDistanceFromDistanceFromXY。 每個都會傳回雙精度浮點,計算從到零的距離、從指定點到點的距離,以及從指定的 X 和 Y 座標到 Point 的距離。 DistanceDistanceFrom 每個呼叫 DistanceFromXY,並示範如何針對每個方法使用不同的自變數。

' Distance from 0 to Point.  
<SqlMethod(OnNullCall:=False)> _  
Public Function Distance() As Double  
    Return DistanceFromXY(0, 0)  
End Function  
  
' Distance from Point to the specified point.  
<SqlMethod(OnNullCall:=False)> _  
Public Function DistanceFrom(ByVal pFrom As Point) As Double  
    Return DistanceFromXY(pFrom.X, pFrom.Y)  
End Function  
  
' Distance from Point to the specified x and y values.  
<SqlMethod(OnNullCall:=False)> _  
Public Function DistanceFromXY(ByVal ix As Int32, ByVal iy As Int32) _  
    As Double  
    Return Math.Sqrt(Math.Pow(ix - _x, 2.0) + Math.Pow(iy - _y, 2.0))  
End Function  
// Distance from 0 to Point.  
[SqlMethod(OnNullCall = false)]  
public Double Distance()  
{  
    return DistanceFromXY(0, 0);  
}  
  
// Distance from Point to the specified point.  
[SqlMethod(OnNullCall = false)]  
public Double DistanceFrom(Point pFrom)  
{  
    return DistanceFromXY(pFrom.X, pFrom.Y);  
}  
  
// Distance from Point to the specified x and y values.  
[SqlMethod(OnNullCall = false)]  
public Double DistanceFromXY(Int32 iX, Int32 iY)  
{  
    return Math.Sqrt(Math.Pow(iX - _x, 2.0) + Math.Pow(iY - _y, 2.0));  
}  

使用 SqlMethod 屬性

Microsoft.SqlServer.Server.SqlMethodAttribute 類別提供自定義屬性,可用來標記方法定義,以便在 Null 呼叫行為上指定決定性,以及指定方法是否為 Mutator。 會假設這些屬性的預設值,而且只有在需要非預設值時,才會使用自定義屬性。

注意

SqlMethodAttribute 類別繼承自 SqlFunctionAttribute 類別,因此 SqlMethodAttribute 會繼承 SqlFunctionAttributeFillRowMethodNameTableDefinition 字段。 這表示可以撰寫數據表值方法,這不是這種情況。 方法會編譯和元件部署,但在運行時間引發有關 IEnumerable 傳回類型的錯誤,並出現下列訊息:「元件 』<assembly>' 類別 'class>' 中的方法、屬性或字段 '<<name>' 具有無效的傳回類型。」

下表說明可用於 UDT 方法的一些相關 Microsoft.SqlServer.Server.SqlMethodAttribute 屬性,並列出其預設值。

DataAccess
指出函式是否牽涉到存取儲存在 SQL Server 本機實例中的用戶數據。 預設值為 DataAccessKind

IsDeterministic
指出函式是否產生相同的輸出值,並指定相同的輸入值和相同的資料庫狀態。 預設值為 false

IsMutator
指出方法是否在 UDT 實例中造成狀態變更。 預設值為 false

IsPrecise
指出函式是否牽涉到不精確計算,例如浮點運算。 預設值為 false

OnNullCall
指出指定 Null 參考輸入自變數時,是否呼叫 方法。 默認值為 true

範例

Microsoft.SqlServer.Server.SqlMethodAttribute.IsMutator 屬性可讓您標記方法,以允許變更 UDT 實例的狀態。 Transact-SQL 不允許在一個 UPDATE 語句的 SET 子句中設定兩個 UDT 屬性。 不過,您可以讓方法標示為變更兩個成員的 Mutator。

注意

查詢中不允許 Mutator 方法。 它們只能在指派語句或數據修改語句中呼叫。 如果標示為 mutator 的方法不會傳回 void (或不是 Visual Basic 中的 Sub ),CREATE TYPE 會失敗並出現錯誤。

下列語句假設存在具有 Rotate 方法的三角形 UDT 下列 Transact-SQL update 語句會叫用 Rotate 方法:

UPDATE Triangles SET t.RotateY(0.6) WHERE id=5  

Rotate 方法會以 SqlMethod 屬性設定 IsMutator 裝飾為 true,讓 SQL Server 可以將方法標示為 Mutator 方法。 程序代碼也會將 OnNullCall 設定為 false,這表示如果任何輸入參數都是 Null 參考,則方法會傳回 Null 參考(Visual Basic 中的 Nothing)。

<SqlMethod(IsMutator:=True, OnNullCall:=False)> _  
Public Sub Rotate(ByVal anglex as Double, _  
  ByVal angley as Double, ByVal anglez As Double)   
   RotateX(anglex)  
   RotateY(angley)  
   RotateZ(anglez)  
End Sub  
[SqlMethod(IsMutator = true, OnNullCall = false)]  
public void Rotate(double anglex, double angley, double anglez)   
{  
   RotateX(anglex);  
   RotateY(angley);  
   RotateZ(anglez);  
}  

實作具有使用者定義格式的UDT

使用使用者定義的格式實作 UDT 時,您必須實作可實 作 Microsoft.SqlServer.Server.IBinarySerialize 介面的讀取寫入 方法,以處理串行化和還原串行化 UDT 數據。 您也必須指定 Microsoft.SqlServer.Server.SqlUserDefinedTypeAttributeMaxByteSize 属性。

貨幣 UDT

貨幣 UDT 隨附於可隨 SQL Server 一起安裝的 CLR 範例,從 SQL Server 2005 (9.x) 開始。

貨幣 UDT 支援處理特定文化特性貨幣系統中的貨幣金額。 您必須定義兩個欄位:CultureInfo字串串,指定發行貨幣的人員(例如 en-us)和 CurrencyValue 的小點,也就是貨幣金額。

雖然伺服器不會使用它來執行比較, 但 Currency UDT 會實 作 System.IComparable 介面,這會公開單一方法 System.IComparable.CompareTo。 在想要正確地比較或排序文化特性內的貨幣值時,這會在用戶端上使用。

在 CLR 中執行的程式代碼會分別比較文化特性與貨幣值。 針對 Transact-SQL 程式代碼,下列動作會決定比較:

  1. IsByteOrdered 屬性設定為 true,這會指示 SQL Server 在磁碟上使用保存的二進位表示法進行比較。

  2. 使用 Currency UDT 的 Write 方法,判斷 UDT 如何保存在磁碟上,因此如何比較和排序 Transact-SQL 作業的 UDT 值。

  3. 使用下列二進位格式儲存 貨幣 UDT:

    1. 將文化特性儲存為 UTF-16 編碼字串,以使用 Null 字元填補右邊的位元組 0-19。

    2. 使用位元組 20 和更新版本來包含貨幣的十進位值。

填補的目的是確保文化特性與貨幣值完全分開,因此,當一個 UDT 與 Transact-SQL 程式代碼中的另一個 UDT 進行比較時,文化特性位元組會與文化特性位元組進行比較,而貨幣位元組值則會與貨幣位元組值進行比較。

貨幣屬性

貨幣 UDT 是使用下列屬性所定義。

<Serializable(), Microsoft.SqlServer.Server.SqlUserDefinedType( _  
    Microsoft.SqlServer.Server.Format.UserDefined, _  
    IsByteOrdered:=True, MaxByteSize:=32), _  
    CLSCompliant(False)> _  
Public Structure Currency  
Implements INullable, IComparable, _  
Microsoft.SqlServer.Server.IBinarySerialize  
[Serializable]  
[SqlUserDefinedType(Format.UserDefined,   
    IsByteOrdered = true, MaxByteSize = 32)]  
    [CLSCompliant(false)]  
    public struct Currency : INullable, IComparable, IBinarySerialize  
    {  

使用 IBinarySerialize 建立讀取和寫入方法

當您選擇 [使用者定義 串行化格式] 時,也必須實 作 IBinarySerialize 介面,並建立您自己的 讀取寫入 方法。 貨幣 UDT 中的下列程式會使用 System.IO.BinaryReaderSystem.IO.BinaryWriter 來讀取和寫入 UDT。

' IBinarySerialize methods  
' The binary layout is as follow:  
'    Bytes 0 - 19: Culture name, padded to the right with null  
'    characters, UTF-16 encoded  
'    Bytes 20+: Decimal value of money  
' If the culture name is empty, the currency is null.  
Public Sub Write(ByVal w As System.IO.BinaryWriter) _  
  Implements Microsoft.SqlServer.Server.IBinarySerialize.Write  
    If Me.IsNull Then  
        w.Write(nullMarker)  
        w.Write(System.Convert.ToDecimal(0))  
        Return  
    End If  
  
    If cultureName.Length > cultureNameMaxSize Then  
        Throw New ApplicationException(String.Format(CultureInfo.CurrentUICulture, _  
           "{0} is an invalid culture name for currency as it is too long.", cultureNameMaxSize))  
    End If  
  
    Dim paddedName As String = cultureName.PadRight(cultureNameMaxSize, CChar(vbNullChar))  
  
    For i As Integer = 0 To cultureNameMaxSize - 1  
        w.Write(paddedName(i))  
    Next i  
  
    ' Normalize decimal value to two places  
    currencyVal = Decimal.Floor(currencyVal * 100) / 100  
    w.Write(currencyVal)  
End Sub  
  
Public Sub Read(ByVal r As System.IO.BinaryReader) _  
  Implements Microsoft.SqlServer.Server.IBinarySerialize.Read  
    Dim name As Char() = r.ReadChars(cultureNameMaxSize)  
    Dim stringEnd As Integer = Array.IndexOf(name, CChar(vbNullChar))  
  
    If stringEnd = 0 Then  
        cultureName = Nothing  
        Return  
    End If  
  
    cultureName = New String(name, 0, stringEnd)  
    currencyVal = r.ReadDecimal()  
End Sub  
  
// IBinarySerialize methods  
// The binary layout is as follow:  
//    Bytes 0 - 19:Culture name, padded to the right   
//    with null characters, UTF-16 encoded  
//    Bytes 20+:Decimal value of money  
// If the culture name is empty, the currency is null.  
public void Write(System.IO.BinaryWriter w)  
{  
    if (this.IsNull)  
    {  
        w.Write(nullMarker);  
        w.Write((decimal)0);  
        return;  
    }  
  
    if (cultureName.Length > cultureNameMaxSize)  
    {  
        throw new ApplicationException(string.Format(  
            CultureInfo.InvariantCulture,   
            "{0} is an invalid culture name for currency as it is too long.",   
            cultureNameMaxSize));  
    }  
  
    String paddedName = cultureName.PadRight(cultureNameMaxSize, '\0');  
    for (int i = 0; i < cultureNameMaxSize; i++)  
    {  
        w.Write(paddedName[i]);  
    }  
  
    // Normalize decimal value to two places  
    currencyValue = Decimal.Floor(currencyValue * 100) / 100;  
    w.Write(currencyValue);  
}  
public void Read(System.IO.BinaryReader r)  
{  
    char[] name = r.ReadChars(cultureNameMaxSize);  
    int stringEnd = Array.IndexOf(name, '\0');  
  
    if (stringEnd == 0)  
    {  
        cultureName = null;  
        return;  
    }  
  
    cultureName = new String(name, 0, stringEnd);  
    currencyValue = r.ReadDecimal();  
}  

另請參閱

建立使用者定義類型