2011-03-23 85 views
5

我試圖在C#標識符中輸入非ASCII字符,並且程序編譯並運行得很好(至少乍一看)。更確切地說,我在枚舉中使用了克羅地亞變元字符(čćđšž)。我真的需要使用這些特殊字符,因爲我將字符串枚舉映射爲NHibernate中的值對象。將這些標準字符作爲查詢顯示給用戶時,必須避免使用這些標準字符真的很難看。在我開始大規模使用枚舉之前,我真的需要知道這種編程技術(特別是關於NHibernate)是否存在任何含義(隱藏陷阱)?或者,如果您有更好的方式處理此問題,請告訴我。C#標識符中的非ASCII字符引用NHibernate

此外,是否有與用於重構,SVN等

+0

你可以使用Html字符嗎?這是一個網絡應用程序? – hunter 2011-03-23 19:39:03

+1

如果將「數據」放在枚舉名稱中,有些東西聽起來很有趣。不確定NHibernate是如何工作的,但確實聽起來不錯。 (還要考慮ASCII /英文通常是最小公分母代碼格式)。有趣的關於語言支持國際化的問題,雖然(+1) - 希望看到規範退出。 – 2011-03-23 19:39:05

+0

@ hunter有趣的問題。這可能是一個問題。我會嘗試使用MVC。 – zszep 2011-03-23 19:44:50

回答

6

C#語言使用Unicode編碼,這意味着你的特殊字符將不會是一個問題的工具的任何問題。但是,我喜歡用英文保留我的代碼,而不使用特殊字符。我還沒有找到一個證明使用文化特定命名的問題。

從C#語言規範:

A C#程序由一個或多個 源文件,正式名稱爲 編譯單元(第9.1節)。源 文件是Unicode 字符的有序序列。源文件通常爲 與文件系統中的 文件具有一一對應的關係,但不需要此 對應關係。對於 最大可移植性,建議使用 文件系統中的文件使用UTF-8編碼進行編碼 。

2.1節(Microsoft C# Language Specification

+0

+1 ...現在,快速,章節號和摘錄[規範!](http://www.scribd.com/doc/7783523/C-30-Language-Specification):) – 2011-03-23 19:41:14

+0

我也是這樣,但正如我所說的,我因爲枚舉而被迫進入這個領域。 – zszep 2011-03-23 19:42:50

+0

@Željko:還有其他方法可以將枚舉映射爲值。你不需要使用你的枚舉作爲值。 – 2011-03-23 20:01:07

1

我只是寫的例子在這裏告訴你如何能做到的映射。

考慮您枚舉所有值包含非ASCII字符:

public enum MyEnum 
{ 
    NonAsciiName1, 
    NonAsciiName2, 
    NonAsciiName3, 
} 

你可以改變它做到這一點,所有值都具有ASCII字符:

public enum MyEnum 
{ 
    AsciiName1, 
    AsciiName2, 
    AsciiName3, 
} 

public static class MyEnumExtensions 
{ 
    static readonly Dictionary<MyEnum, string> map = new Dictionary<MyEnum, string> 
    { 
     { MyEnum.AsciiName1, "NonAsciiName1" }, 
     { MyEnum.AsciiName2, "NonAsciiName2" }, 
     { MyEnum.AsciiName3, "NonAsciiName3" }, 
    }; 

    public static string GetValue(this MyEnum key) 
    { 
     return map[key]; 
    } 
} 

那麼你的代碼需要小更改使用此:

// you probably had something like: 
var myEnumValue = MyEnum.NonAsciiName1; 
var value = myEnumValue.ToString(); 

// now becomes: 
var myEnumValue = MyEnum.AsciiName1; 
var value = myEnumValue.GetValue(); 

或者DEHAAS建議使用以類似方式工作的屬性。但是,那麼你必須使用反射來獲得有點性能損失的值。

using System.ComponentModel; 

public enum MyEnum 
{ 
    [DescriptionAttribute("NonAsciiName1")] AsciiName1, 
    [DescriptionAttribute("NonAsciiName2")] AsciiName2, 
    [DescriptionAttribute("NonAsciiName3")] AsciiName3, 
} 

public static class MyEnumExtensions 
{ 
    public static string GetValue(this MyEnum key) 
    { 
     return typeof(MyEnum).GetField(key.ToString()) 
          .GetCustomAttributes(typeof(DescriptionAttribute), false) 
          .Cast<DescriptionAttribute>() 
          .Single() 
          .Description; 
    } 
} 
+0

M這看起來很有前途。我會在早上接下來的事情嘗試。唯一令我擔心的是,Ascii的名字(非克羅地亞語)將被存儲在數據庫中(例如,我們有「muški」(男性)和「ženski」(女性)作爲性別,我必須將它們保存爲「muski」和「zenski」到數據庫中: – zszep 2011-03-23 21:08:17

+0

@Željko:我對nhibernate瞭解不多,但是你可能不需要那麼遠,如果它和LINQ to SQL一樣,你可能會改變映射屬性添加到列,以便您的ASCII屬性映射到您的非ASCII列。我認爲有一種方法可以做到這一點。Tristan的評論表明這完全可能。 – 2011-03-23 21:20:32

+0

M它更像是實體框架,我同意你的看法。也許枚舉不是處理這種情況的最好方法(儘管幾乎每個NHibernate樣本都以這種方式處理它)。我會嘗試另一種方法。 – zszep 2011-03-24 06:33:39

3

我做了這個自定義的用戶類型從枚舉安全轉化爲int的NHibernate.You應該改變使用字符串,而不是INT功能。然後,您可以更改方法「NullSafeGet」以將數據庫值轉換爲您的枚舉值。這樣做,你可以在你的數據庫中爲你的枚舉值和用戶可讀的名字使用普通字符。 編輯:我自己做了修改。您可以將此類用於您的自定義用戶類型。你應該實現「SpecialConversion」方法來從你的特殊字符串轉換爲enum,反之亦然。

public class EnumToSpecialStringType<TEnum> : IUserType 
{ 
    #region IUserType Members 

    /// <summary> 
    /// Reconstruct an object from the cacheable representation. At the very least this 
    /// method should perform a deep copy if the type is mutable. (optional operation) 
    /// </summary> 
    /// <param name="cached">the object to be cached</param> 
    /// <param name="owner">the owner of the cached object</param> 
    /// <returns>a reconstructed object from the cachable representation</returns> 
    public object Assemble(object cached, object owner) 
    { 
     return DeepCopy(cached); 
    } 

    /// <summary> 
    /// Return a deep copy of the persistent state, stopping at entities and at collections. 
    /// </summary> 
    /// <param name="value">generally a collection element or entity field</param> 
    /// <returns>a copy</returns> 
    public object DeepCopy(object value) 
    { 
     return value; 
    } 

    /// <summary> 
    /// Transform the object into its cacheable representation. At the very least this 
    /// method should perform a deep copy if the type is mutable. That may not be enough 
    /// for some implementations, however; for example, associations must be cached as 
    /// identifier values. (optional operation) 
    /// </summary> 
    /// <param name="value">the object to be cached</param> 
    /// <returns>a cacheable representation of the object</returns> 
    public object Disassemble(object value) 
    { 
     return DeepCopy(value); 
    } 

    /// <summary> 
    /// Compare two instances of the class mapped by this type for persistent "equality" 
    /// ie. equality of persistent state 
    /// </summary> 
    /// <param name="x"/> 
    /// <param name="y"/> 
    /// <returns/> 
    public new bool Equals(object x, object y) 
    { 
     if (ReferenceEquals(x, y)) return true; 
     if (x == null || y == null) return false; 
     return x.Equals(y); 
    } 

    /// <summary> 
    /// Get a hashcode for the instance, consistent with persistence "equality" 
    /// </summary> 
    public int GetHashCode(object x) 
    { 
     return x.GetHashCode(); 
    } 

    /// <summary> 
    /// Are objects of this type mutable? 
    /// </summary> 
    public bool IsMutable 
    { 
     get { return false; } 
    } 

    /// <summary> 
    /// Retrieve an instance of the mapped class from a ADO.NET resultset. 
    /// Implementors should handle possibility of null values. 
    /// </summary> 
    /// <param name="rs">a IDataReader</param> 
    /// <param name="names">column names</param> 
    /// <param name="owner">the containing entity</param> 
    /// <returns/> 
    /// <exception cref="HibernateException">HibernateException</exception> 
    public object NullSafeGet(IDataReader rs, string[] names, object owner) 
    { 
     var value = NHibernateUtil.String.NullSafeGet(rs, names[0]) as string; 

     // Put you special conversion here (string -> enum) 
     var converted = SpecialConversion(value); 

     return converted; 
    } 

    /// <summary> 
    /// Write an instance of the mapped class to a prepared statement. 
    /// Implementors should handle possibility of null values. 
    /// A multi-column type should be written to parameters starting from index. 
    /// </summary> 
    /// <param name="cmd">a IDbCommand</param> 
    /// <param name="value">the object to write</param> 
    /// <param name="index">command parameter index</param> 
    /// <exception cref="HibernateException">HibernateException</exception> 
    public void NullSafeSet(IDbCommand cmd, object value, int index) 
    { 
     var parameter = (IDataParameter)cmd.Parameters[index]; 

     // Do conversion from your enum to database string value 
     var converted = SpecialConversion(value); 

     parameter.Value = converted; 
    } 

    /// <summary> 
    /// During merge, replace the existing (<paramref name="target"/>) value in the entity 
    /// we are merging to with a new (<paramref name="original"/>) value from the detached 
    /// entity we are merging. For immutable objects, or null values, it is safe to simply 
    /// return the first parameter. For mutable objects, it is safe to return a copy of the 
    /// first parameter. For objects with component values, it might make sense to 
    /// recursively replace component values. 
    /// </summary> 
    /// <param name="original">the value from the detached entity being merged</param> 
    /// <param name="target">the value in the managed entity</param> 
    /// <param name="owner">the managed entity</param> 
    /// <returns>the value to be merged</returns> 
    public object Replace(object original, object target, object owner) 
    { 
     return original; 
    } 

    /// <summary> 
    /// The type returned by <c>NullSafeGet()</c> 
    /// </summary> 
    public Type ReturnedType 
    { 
     get 
     { 
      return typeof(TEnum); 
     } 
    } 

    /// <summary> 
    /// The SQL types for the columns mapped by this type. 
    /// </summary> 
    public SqlType[] SqlTypes 
    { 
     get 
     { 
      return new[] { new SqlType(DbType.String) }; 
     } 
    } 

    #endregion 
} 

我用流利的NHibernate和我的方式,我的專欄與此自定義的用戶類型映射是:

Map(x => x.PropertyName, "ColumnName").CustomType<EnumToSpecialStringType<EnumType>>(); 

編輯:這裏有一個關於如何使用XML映射文件映射一個用戶類型後:

nHibernate mapping to custom types

+0

任何想法如何映射這個沒有流利的NHibernate? – zszep 2011-03-23 21:11:30

+0

@ŽeljkoSzep我從來沒有使用過一個xml映射文件。我更新了我的答案,以提供另一個stackoverflow帖子的鏈接。我希望另一篇文章會告訴你如何! – 2011-03-23 21:26:26

1

是的,我認爲這是一個陷阱。

向用戶顯示查找並保存數據庫中可能(唯一)項目的列表可以更好地單獨保存。特別是如果您的應用程序將來支持其他語言。

不是很面向對象,你可以在你的代碼,一類是你的枚舉和各自的語言在一個NHibernate的映射這樣的價值,就像之間的直接映射:

<class name="DropDown" table="DropDown_Language"> 
    <!-- map onto the ENUMeration --> 
    <id name="UniqueID" column="Id" type="Int32" > 
     <generator class="identity" /> 
    </id> 
    <property name="DropDownType" column="DropDownTypeId" type="DropDownType, Domain"/> 
    <property name="Language" column="LanguageId" type="LanguageReferenceType, "/> 
    <property name="DropDownTypeDescription" column="DropDownTypeDescription" type="string"/> 
</class> 

在DropDownTypeDescription列,然後你會把下拉列表的值。

希望我明白你的問題:-)

+0

是的,但我不想映射完整的成長實體。這是簡單的價值對象,我想要映射2或3個可能的值(如性別,服裝尺寸:S M L XL) – zszep 2011-03-23 21:33:29

+0

我明白了。只是想要關注您在單獨映射Croation枚舉時可能遇到的未來擴展問題,然後您必須用其他語言的應用程序出國。 (在我們的代碼庫中,我們確實有Gender映射) – Hace 2011-03-23 21:42:40

+0

我同意你的看法,國際化將成爲PITA和我的枚舉方法。如果你查看Jeff M的回答,這可以解決我的問題(使用ascii枚舉並且有一個本地化的字符串用於演示目的,可以從資源文件中讀取)。 – zszep 2011-03-23 21:47:03