c# - ReSharper-Posible asignación nula al usar Microsoft.Contracts
code-contracts design-by-contract (7)
¿Hay alguna forma de indicarle a ReSharper que no se producirá una referencia nula debido a los requisitos de verificación de Diseño por Contrato? Por ejemplo, el siguiente código mostrará la advertencia ( Possible ''null'' assignment to entity marked with ''NotNull'' attribute
) en ReSharper en las líneas 7 y 8:
private Dictionary<string, string> _Lookup = new Dictionary<string, string>();
public void Foo(string s)
{
Contract.Requires(!String.IsNullOrEmpty(s));
if (_Lookup.ContainsKey(s))
_Lookup.Remove(s);
}
Lo que es realmente extraño es que si elimina la línea Contract.Requires(...)
, el mensaje ReSharper desaparece.
Actualizar
Encontré la solución a través de ExternalAnnotations que también fue mencionada por Mike a continuación. Aquí hay un ejemplo de cómo hacerlo para una función en Microsoft.Contracts:
- Cree un directorio llamado
Microsoft.Contracts
en el directorio ReSharper deExternalAnnotations
. - A continuación, cree un archivo llamado
Microsoft.Contracts.xml
y rellene así:
<assembly name="Microsoft.Contracts">
<member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
</assembly>
- Reinicie Visual Studio, y el mensaje desaparece!
Creo que puedes pero no es trivial. Eche un vistazo a la ayuda en línea de Resharper para la anotación de código
Anotaron las clases BCL y el marco NUnit (y más) para mejorar las capacidades de inspección de código de Resharpers.
Por ejemplo, con las afirmaciones NUnit que anotaron con un AssertionMethodAttribute. Esto le dice a la inspección del código de Resharpers que si superó un Assert.IsNotNull (foo); entonces foo no debe ser nulo y ya no producirá la advertencia "Posible ''nula'' ...".
Podría producir un archivo xml que anote el método Contracts.Requires para indicar que es como un Assert.
La razón por la que el mensaje desaparece cuando elimina la aserción es que R # funciona en modo "optimista" de forma predeterminada. Asume que todo es nulo hasta que haga algo que indique que puede ser nulo. Eso es lo que sucede cuando agrega la llamada a String.IsNullOrEmpty
. Estás diciendo que s
podría ser nulo. Simplemente no es consciente de que el método Contract.Requires
detendrá la ejecución si ese es el caso, pero que resolvió con la anotación.
En R # 5.0 puede cambiar a un modo pesimista que asume lo peor en cada esquina.
Me gustaría agregar que para las personas que escriben sus propios métodos de afirmación, puede incluir estos atributos sin un archivo XML externo. En Visual Studio, vaya a ReSharper > Options > Code Annotations
y haga clic en el botón Copy default implementation to clipboard
. Luego cree un nuevo archivo (en cualquier lugar que desee en su solución) y pegue el código desde el portapapeles. Ahora, puedes crear métodos como este:
public class Require
{
[AssertionMethod]
public static void That(
[AssertionCondition(AssertionConditionType.IS_TRUE)]
bool requiredCondition,
string message = null)
{
...
}
...
}
Ahora cualquier llamada a Require.That(a != null)
le indicará a ReSharper que no puede pasar esta línea si a
es nulo. A diferencia de la técnica de anotaciones externas, esto funcionará para cualquier persona que use sus métodos, sin ningún trabajo adicional de su parte.
Actualizar
Resharper ha cambiado su modelo de anotación de contrato a partir de la versión 7. A continuación se muestra el método anterior:
public class Require
{
[ContractAnnotation("requiredCondition:false => halt")]
public static void That(
bool requiredCondition,
string message = null)
{
...
}
...
}
Tomé el XML de Porges y añadí anotaciones para los métodos Assert y Assume. Daré esta respuesta en caso de que otras personas quieran agregar más métodos.
<assembly name="mscorlib">
<member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean, System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean, System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean,System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean,System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean,System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
</assembly>
Nota : a partir del EAP R # 8.0 actual, esta funcionalidad está incluida.
Aquí está la solución para la versión actual (es decir, .NET 4.0) de los contratos de código:
Dentro de .../ExternalAnnotations/mscorlib/Contracts.xml
, agregue lo siguiente:
<assembly name="mscorlib">
<member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean, System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean, System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean,System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean,System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean,System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
</assembly>
Resharper ha cambiado su modelo de anotación de contrato a partir de la versión 7.
Necesitas un archivo diferente. La nueva ubicación (supongo que solo para las aplicaciones de Metro) es: "C: / Archivos de programa (x86) / JetBrains / ReSharper / v7.1 / Bin / ExternalAnnotations / .NETCore / System.Diagnostics.Contracts / Contracts.xml"
Estoy usando Visual Studio 2012 y .Net 4.5 y Resharper 7.1.
Contenido:
<assembly name="System.Diagnostics.Contracts">
<member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean, System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assume(System.Boolean, System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean,System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Requires``1(System.Boolean,System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Invariant(System.Boolean,System.String)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<attribute ctor="M:JetBrains.Annotations.ContractAnnotationAttribute.#ctor(System.String)">
<argument>condition:false=>halt</argument>
</attribute>
</member>
</assembly>
TL; DR: agrega el símbolo de compilación condicional CONTRACTS_FULL
a tu proyecto.
El método Contract.Requires(...)
está vacío e inhabilitado, a menos que habilite y use el editor de códigos de contactos. Al ejecutar la reescritura manualmente, o (normalmente) habilitarla a través de las propiedades del proyecto de Visual Studio, mantendrá el código Contract.Requires(...)
en sus archivos binarios compilados y reescritos. Sabes que el código funcionará, e ignorando la advertencia de Resharper, puedes ejecutarlo y probar.
¿Cual es el problema entonces? Resharper no sabe que los contratos de código se están ejecutando, ya que en realidad solo se inyectan en el momento (posterior) a la compilación. A los ojos de Resharper, está deshabilitado de la misma manera que funciona el símbolo del preprocesador DEBUG
y cómo Visual Studio elimina las áreas de su código que no formarán parte de sus archivos binarios compilados.
#ifdef DEBUG
Console.WriteLine("I''m in DEBUG mode, so this is probably a Debug build.");
#else
Console.WriteLine("Let''s assume this is a Release build.");
#endif
De acuerdo con el manual del usuario de los contratos de código (capítulo 2, primer párrafo) y el código fuente en ContractExtensions.cs
(incluido en la carpeta de instalación de los contratos de código), es necesario establecer CONTRACTS_FULL
antes de compilar con él. Los métodos de contrato se implementan realmente con [ConditionalAttribute("CONTRACTS_FULL")]
y se ignoran (no se incluyen en el momento de la compilación) a menos que se establezca el indicador. Resharper respeta esta bandera y asume que la función no se ejecutará a menos que esté configurada.
[ConditionalAttribute("CONTRACTS_FULL")]
public static void Requires(bool condition) { ... }
Solución: agregue el símbolo de compilación condicional CONTRACTS_FULL
a su proyecto. Consulte Uso de Code Contracts Visual Studio y con Resharper por Henning Krause.
El equipo de Resharper ha sido notificado; El análisis de código no considera la configuración en la pestaña de propiedades del proyecto ''Contratos de código'' , Soporte de contratos de código de Microsoft .