2011-03-23 20 views
5

He intentado introducir caracteres no ASCII en identificadores C#, y el programa se compila y funciona correctamente (al menos a primera vista). Para ser más precisos, uso caracteres diacríticos croatas (čćđšž) en enumeraciones. Realmente necesito usar estos caracteres especiales porque mapeo enumeraciones de cadenas como objetos de valor en NHibernate. Sería realmente feo tener que evitar estos caracteres estándar al mostrarlos como búsquedas para el usuario. Antes de comenzar a usar enumeraciones de esta manera a gran escala, realmente necesito saber si hay alguna implicación (trampas ocultas) en una técnica de programación como esta (especialmente con respecto a NHibernate). O bien, si tiene una forma mejor de manejar esto, hágamelo saber.Caracteres no ASCII en identificadores C# Referenciados por NHibernate

También, hay algún problema con las herramientas utilizadas para la refactorización, etc SVN

+0

¿Puede usar el carácter Html? es esta una aplicación web? – hunter

+1

Algo suena gracioso si pone "datos" en nombres enum. No estoy seguro de cómo funciona NHibernate, pero realmente suena. (También considere que ASCII/English es generalmente el formato de código de denominador común más bajo). Sin embargo, una pregunta interesante sobre el soporte de idiomas para la internacionalización (+1) - espero ver la especificación extraída. –

+0

@hunter Interesante pregunta. Esto podria ser un problema. Lo probaré con MVC. – zszep

Respuesta

6

El lenguaje C# utiliza la codificación Unicode, lo que significa que su carácter especial no será un problema. Sin embargo, me gusta mantener mi código en inglés sin caracteres especiales. Todavía tengo que encontrar un problema que justifique el uso de nombres específicos de cultura.

De la especificación del lenguaje C#:

un programa en C# se compone de uno o más archivos de origen , conocido formalmente como unidades de compilación (§9.1). Un archivo fuente es una secuencia ordenada de caracteres Unicode . Los archivos de origen típicamente tienen una correspondencia de uno a uno con los archivos en un sistema de archivos, pero esta correspondencia no es necesaria. Para máxima portabilidad, se recomienda que los archivos en un sistema de archivos estén codificados con la codificación UTF-8.

Sección 2,1 (Microsoft C# Language Specification)

+0

+1 ... ahora, rápido, un número de sección y un extracto de [la especificación !] (http://www.scribd.com/doc/7783523/C-30-Language-Specification) :) –

+0

Yo también, pero como ya he dicho, estoy bastante obligado a esto debido a las enumeraciones. – zszep

+0

@Željko: hay otras formas en que puede asignar enumeraciones a valores. No necesita utilizar su enumeración como valor. –

1

sólo estoy escribiendo aquí ejemplos para mostrar cómo se puede hacer el mapeo.

Tenga en cuenta su enumeración, donde todos los valores contiene caracteres no ASCII:

public enum MyEnum 
{ 
    NonAsciiName1, 
    NonAsciiName2, 
    NonAsciiName3, 
} 

Se podría cambiarlo para hacer esto que todos los valores tienen caracteres 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]; 
    } 
} 

A continuación, el código podría necesitar pequeña cambios a utilizar esto:

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

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

O si se ha sugerido DEHAAS, use atributos que funcionen de manera similar. Pero entonces tendrías que usar la reflexión para obtener los valores que tienen un poco de penalización de rendimiento.

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 Esto parece prometedor. Voy a intentarlo a continuación en la mañana. Lo único que me preocupa es que los nombres Ascii (no croatas) se guarden en la base de datos (por ejemplo, tenemos "muški" (masculino) y "ženski" (femenino) como género. Tendría que guardarlos como "muski" y "zenski" a la base de datos. – zszep

+0

@Željko: No sé mucho sobre el nhibernate, pero probablemente no tenga que ir tan lejos. Si es algo así como LINQ to SQL, probablemente podría cambiar el mapeo de propiedades a las columnas para que sus propiedades ASCII se correlacionen con sus columnas que no sean ASCII. Creo que hay una manera de hacerlo. El comentario de Tristan sugiere que esto es completamente posible. –

+0

M Es más como el marco Entity. Estoy de acuerdo con usted en esto.Tal vez las enumeraciones no son la mejor manera de manejar este escenario (aunque casi todas las muestras de NHibernate lo manejan de esta manera). Voy a intentar otro enfoque. – zszep

3

he hecho este tipo de usuario personalizado para convertir de forma segura desde enum a int para NHibernate.You debe cambiar la función a usar cadenas en lugar de int. Luego puede cambiar el método "NullSafeGet" para hacer la conversión del valor de la base de datos a su valor enum. De esta manera, podría usar caracteres normales para sus valores enum y nombres legibles por el usuario en su base de datos. EDITAR: Hice los cambios yo mismo. Puede usar esta clase para su tipo de usuario personalizado. Debe implementar el método "SpecialConversion" para convertir de su cadena especial a enum y viceversa.

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 
} 

utilizo fluidez NHibernate y mi forma de asignar mi columna con este tipo de usuario personalizada fue:

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

EDIT: Aquí hay un post acerca de cómo asignar un tipo de usuario utilizando un archivo de asignación XML :

nHibernate mapping to custom types

+0

¿Alguna idea de cómo mapear esto sin fluidez NHibernate? – zszep

+0

@ Željko Szep Nunca utilicé un archivo de mapeo xml. Actualicé mi respuesta para dar un enlace a otra publicación de stackoverflow. ¡Espero que la otra publicación te muestre cómo! –

1

Sí, considero que una trampa.

Mostrar las búsquedas al usuario y mantener una lista de los elementos posibles (únicos) en la base de datos se pueden guardar mejor por separado. Especialmente si su aplicación es para admitir otros idiomas en el futuro.

No muy orientado a objetos que podría tener una clase en su código que es una asignación directa entre su enumeración y el valor de la lengua respectiva, como en un mapeo nhibernate así:

<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> 

En el DropDownTypeDescription En la columna, pondría los valores del menú desplegable.

Espero haber entendido su pregunta :-)

+0

Sí, pero no quiero mapear entidades completamente desarrolladas. Son objetos de valor simple con 2 o 3 valores posibles que quiero asignar (como el sexo, el tamaño de la ropa: S M L XL) – zszep

+0

Entiendo. Solo quería llamar la atención sobre los futuros problemas de extensión que podría tener cuando correlacione las enums de Croation solo y luego tenga que ir al extranjero con su aplicación en otros idiomas. (en nuestra base de código tenemos un mapa de género) – Hace

+0

Estoy de acuerdo con usted, la internacionalización sería un PITA con el enum enfoque mío. Si busca la respuesta de Jeff M, podría resolver mi problema (use las enumeraciones ascii y tenga una cadena localizada para propósitos de presentación que pueda leerse de un archivo de recursos). – zszep

Cuestiones relacionadas