una sirve que programacion para interfaz interfaces implementacion ejemplos clases clase abstractas abstracta c# linq interface implicit-conversion data-conversion

c# - sirve - Interfaces, herencia, operadores implícitos y conversiones de tipo, ¿por qué es así?



para que sirve una clase abstracta (5)

¿Por qué el primero no funciona?

Porque está pidiendo al tiempo de ejecución dos conversiones implícitas: una para RecurrencePattern y otra para IRecurrencePattern . El tiempo de ejecución solo buscará una relación implícita directa ; no explorará todas las rutas posibles para que solicite que se marche. Supongamos que hubo múltiples conversiones implícitas a diferentes tipos de clases que implementan IRecurrencePattern . ¿Cuál elegiría el tiempo de ejecución? En su lugar, te obliga a especificar los modelos individuales.

Esto se documenta en la Sección 6.4.3 de la especificación del lenguaje C #:

La evaluación de una conversión definida por el usuario nunca implica más de un operador de conversión elevado o definido por el usuario. En otras palabras, una conversión del tipo S al tipo T nunca ejecutará primero una conversión definida por el usuario de S a X y luego ejecutará una conversión definida por el usuario de X a T.

Estoy trabajando con una biblioteca de clase llamada DDay ICal. Es un contenedor C # para el sistema iCalendar implementado en calendarios de Outlook y muchos muchos más sistemas. Mi pregunta se deriva de un trabajo que estaba haciendo con este sistema.

Hay 3 objetos en cuestión aquí

  • IRecurrencePattern - Interfaz
  • RecurrencePattern - Implementación de la Interfaz IRecurrencePattern
  • DbRecurPatt - Clase personalizada que tiene un operador de tipo implícito

IRecurrencePattern: no se muestra todo el código

public interface IRecurrencePattern { string Data { get; set; } }

RecurrencePattern: no se muestra todo el código

public class RecurrencePattern : IRecurrencePattern { public string Data { get; set; } }

DbRecurPatt: no se muestra todo el código

public class DbRecurPatt { public string Name { get; set; } public string Description { get; set; } public static implicit operator RecurrencePattern(DbRecurPatt obj) { return new RecurrencePattern() { Data = $"{Name} - {Description}" }; } }

La parte confusa: a través del sistema DDay.ICal, utilizan IList s para contener una colección de patrones de recurrencia para cada evento en el calendario, la clase personalizada se usa para obtener información de una base de datos y luego se convierte en el patrón de recurrencia. El operador de conversión de tipo implícito.

Pero en el código, noté que seguía fallando al convertir a la List<IRecurrencePattern> de una List<DbRecurPatt> Me di cuenta de que necesitaba convertir a RecurrencePattern , luego convertir a IRecurrencePattern (ya que hay otras clases que implementan IRecurrencePattern diferente También se incluye en la colección.

var unsorted = new List<DbRecurPatt>{ new DbRecurPatt(), new DbRecurPatt() }; var sorted = unsorted.Select(t => (IRecurrencePattern)t);

El código anterior no funciona, IRecurrencePattern un error en IRecurrencePattern .

var sorted = unsorted.Select(t => (IRecurrencePattern)(RecurrencePattern)t);

Esto sí funciona, así que la pregunta que tengo es; ¿Por qué el primero no funciona? (¿Y hay una manera de mejorar este método?)

Creo que podría ser porque el operador implícito está en el objeto RecurrencePattern y no en la interfaz, ¿es correcto? (Soy nuevo en interfaces y operadores implícitos)


... y para responder a su pregunta final sobre el operador implícito: no, no puede definir un operador implícito en una interfaz. Ese tema se trata con más detalle en esta pregunta:

Operador implícito utilizando interfaces.


Básicamente le has pedido al compilador que haga esto:

  1. Tengo esto: DbRecurPatt
  2. Quiero esto: IRecurrencePattern
  3. Por favor, averigüe la forma de llegar del punto 1 al punto 2.

El compilador, aunque solo tenga una opción, no le permite hacer esto. El operador de cast dice específicamente que DbRecurPatt se puede convertir en un RecurrencePattern , no en un IRecurrencePattern .

El compilador solo verifica si uno de los dos tipos involucrados especifica una regla sobre cómo convertir de uno a otro, no permite pasos intermedios.

Dado que no se ha definido ningún operador que permita que DbRecurPatt se convierta directamente a IRecurrencePattern , el compilador compilará esto como un disco duro, reinterpretando la referencia como una referencia a través de una interfaz, que fallará en tiempo de ejecución.

Entonces, la siguiente pregunta sería esta: ¿Cómo puedo hacer esto? Y la respuesta es que no puedes.

El compilador no le permite definir un operador de conversión definido por el usuario desde o hacia una interfaz. Una pregunta diferente aquí en tiene más información .

Si intentas definir tal operador:

public static implicit operator IRecurrencePattern(DbRecurPatt obj) { return new RecurrencePattern() { Data = $"{obj.Name} - {obj.Description}" }; }

El compilador dirá esto:

CS0552
''DbRecurPatt.implicit operator IRecurrencePattern (DbRecurPatt)'': no ​​se permiten conversiones definidas por el usuario hacia o desde una interfaz


Como otros ya han señalado, no puedes hacer un salto directo de DbRecurPatt a IRecurrencePattern . Esta es la razón por la que terminas con este reparto doble muy feo:

var sorted = unsorted.Select(t => (IRecurrencePattern)(RecurrencePattern)t);

Pero, para completar, debe mencionarse que es posible pasar de un DbRecurPatt a un IRecurrencePattern sin ningún cambio con su diseño actual. Es solo que para hacerlo, necesitas dividir tu expresión en varias declaraciones, y al hacerlo, el código se vuelve considerablemente más feo.

Aún así, es bueno saber que puedes hacer esto sin ningún reparto:

var sorted = unsorted.Select( t => { RecurrencePattern recurrencePattern = t; // no cast IRecurrencePattern recurrencePatternInterface = recurrencePattern; // no cast here either return recurrencePatternInterface; });

EDITAR

Gracias a la respuesta de Bill Nadeau por la idea. También puede beneficiarse de la conversión implícita y sus garantías de tiempo de compilación al tiempo que mantiene el código bastante elegante escribiéndolo de esta manera:

var sorted = unsorted .Select<DbRecurPatt, RecurrencePattern>(t => t) // implicit conversion - no cast .Select<RecurrencePattern, IRecurrencePattern>(t => t); // implicit conversion - no cast


Hay otro camino para lograr lo que quieres. Especifique específicamente sus argumentos genéricos en sus llamadas de método en lugar de permitir que el compilador infiera sus argumentos genéricos. Seguirá evitando el lanzamiento, y puede ser un poco menos detallado que algunas de las otras opciones. La única advertencia es que debe incluir una declaración Linq adicional, que resolverá su lista, si eso importa.

var sorted = unsorted .Select<DbRecurPatt, RecurrencePattern>(t => t) .ToList<IRecurrencePattern>();

También puede combinar esta respuesta con sstan para evitar la declaración Linq extra.