2012-01-12 15 views
8

Podría estar malinterpretando contratos de código, pero esta es mi situación.Código de contratos y problemas de herencia, ¿qué va a dónde?

Tengo el siguiente código:

interface IFetch<T> // defined in another DLL 
{ 
    T Fetch(int id); 
} 

interface IUserFetch : IFetch<User> 
{ 
    IEnumerable<User> GetUsersLoggedIn(); 
} 

class UserFetch : IUserFetch 
{ 
    public User Fetch(int id) 
    { 
     return (User) Database.DoStuff (id); 
    } 

    public IEnumerable<User> GetUsersLoggedIn() 
    { 
     return (IEnumerable<User>) Database.DoMoreStuff(); 
    } 
} 

Estoy intentando añadir un contrato relativamente simple: Contract.Requires (id != 0);, y lo quiero validado en Fetch. Cuando lo agregue directamente a Fetch, aparece la advertencia que dice Method Fetch(int id) implements interface 3rdParty.IFetch<User> and thus cannot add Requires.

Creé una clase abstracta de contratos de código implementando IFetch y la apunté a/desde UserFetch usando los atributos ContractClass y ContractClassFor respectivamente. Todavía obtengo un error como CodeContracts: The class 'FetchUserContracts' is supposed to be a contract class for '3rdParty.IFetch<User>', but that type does not point back to this class. Sin embargo, dado que 3rdParty.Fetch es un tipo genérico, no creo que pueda ponerle un código específico.

¿Se ha aclarado el problema? Si es así, ¿cómo lo resuelvo?

+0

Posible duplicado: http://stackoverflow.com/questions/3414586 –

+0

@RobertHarvey: Lo tomaré principalmente, excepto que mi problema parece provenir más de los genéricos –

Respuesta

5

creo que la respuesta de Ɖiamond ǤeezeƦ es correcta. Solo agregaré un poco de explicación.

No se puede agregar una decoración Contract.Requires a un método en un tipo construido cerrado (como IFetch<User>). Debe agregarlo al tipo abierto construido (IFetch<>). La razón de esto es exactamente la misma razón por la que no puede agregar una decoración Contract.Requires a un método concreto que se utiliza para implementar una interfaz: los contratos de código están diseñados para ser verificables en tiempo de compilación cuando el tipo completo de una instancia de objeto puede ser desconocido.

Supongamos que le permitió poner el contrato en la implementación del método concreto.

public User Fetch(int id) 
{ 
    Contract.Requires (id != 0);, 
    return (User) Database.DoStuff (id); 
} 

Supongamos ahora que alguien intentó usar una variable del tipo de interfaz.

class DataDisplayer<T> 
{ 
    Label myLabel = new Label(); 
    public void Display(IFetch<T> fetch, int id) 
    { 
     myLabel.Text = fetch.Fetch(id).ToString(); 
    } 
} 

¿Esto permite una violación del contrato o no? Es imposible saberlo, porque no sabemos cuál será el tipo concreto de fetch en tiempo de ejecución.

Poner el contrato en un tipo cerrado construido particular como IFetch<User> no resuelve este problema. Todavía no sabemos qué tipo de T es en el código de llamada.

El Principio de Sustitución de Liskov, que es una piedra angular de toda la programación orientada a objetos, significa que nunca podemos asumir nada sobre el tipo de tiempo de ejecución de un objeto más allá de lo que indica el tipo de la variable. Y dado que el tipo de variable puede ser una interfaz genérica, los contratos de código no pueden imponer ninguna carga adicional a la persona que llama que lo que está en la definición de la interfaz genérica.

Por lo tanto, Contract.Requires debe colocarse en el tipo construido abierto, de modo que cualquier objeto que implemente la interfaz tenga el requisito, y cualquier posible llamada al método a través de una variable del tipo de interfaz puede verificarse con respecto a a ese requerimiento

10

Es necesario crear una clase abstracta para implementar el contrato, por ejemplo:

[ContractClassFor(typeof(IFetch<>))] 
public abstract class ContractClassForIFetch<T> : IFetch<T> 
{ 
    public T Fetch(int id) 
    { 
     Contract.Requires(id != 0); 
     return default(T); 
    } 
} 

y añadir la ContractClass siguiente atributo a IFetch:

[ContractClass(typeof(ContractClassForIFetch<>))] 
public interface IFetch 
{ 
    T Fetch(int id); 
} 
+0

Ver también este [Q/A relacionado] (https: //stackoverflow.com/questions/3414586/) – orad

Cuestiones relacionadas