developer tip

정말 C #에서 CodeContracts를 좋아하려고합니다.

copycodes 2020. 12. 9. 08:22
반응형

정말 C #에서 CodeContracts를 좋아하려고합니다.


드디어 .NET 3.5 / 4.0 프레임 워크에 추가 된 모든 새로운 기능을 활용하고 있습니다. 지난 며칠 동안 저는 CodeContracts와 함께 일해 왔으며 정말 좋아하기 위해 열심히 노력하고 있습니다. 다른 사람들이 C #에서 CodeContracts 구현에 대해 어떻게 생각하는지 궁금합니다. 특히, 사람들은 인터페이스를위한 Contract 클래스, Contract Invariants 등을위한 계약 메소드를 어떻게 구성하고 있습니까?

계약서가 제공하는 유효성 검사가 마음에 듭니다. 언뜻보기에는 멋져 보입니다. 몇 줄만하면 코드를 실행하기 전에 멋진 빌드 검사를받을 수 있습니다. 불행히도 코드 계약이 C #으로 구현되는 방식이 계약을 문서화하는 것보다 내 코드를 더 복잡하게 만든다는 느낌을 극복하는 데 어려움을 겪고 있습니다. 그리고 계약을 최대한 활용하기 위해 나는 가정과 주장 등으로 내 코드를 어지럽히고 있습니다 (나는 일부 사람들이 좋은 것이라고 말할 것입니다). 하지만 아래의 몇 가지 예가 보여 주듯이, 간단한 한 줄을 4 줄 또는 5 줄로 바꾸고 대체 접근 방식 (예 : 어설 션, 예외 등)에 대한 제 의견으로는 충분한 가치를 추가하지 않습니다.

현재 가장 큰 좌절은 다음과 같습니다.

인터페이스 계약 :

[ContractClass(typeof(IInterfaceContract))]
  public interface IInterface
  {
    Object Method(Object arg);
  }

  [ContractClassFor(typeof(IInterface))]
  internal abstract class IInterfaceContract
  {
    private IInterfaceContract() { }

    Object IInterface.Method(Object arg)
    {
      Contract.Requires(arg != null);
      Contract.Ensures(Contract.Result<Object>() != null);
      return default(Object);
    }
  }

이것은 저에게 그런 단서처럼 느껴집니다. 속성이나 내장 된 언어 지원을 통해 요구 사항을 문서화하는 더 깨끗한 방법이 있었으면합니다. 계약을 지정할 수 있도록 인터페이스를 구현하는 추상 클래스를 구현해야한다는 사실은 기껏해야 지루해 보입니다.

코드 블로 트 :

typeof(Action<>).MakeGenericType(typeof(Object);

쉽게 사용할 수있는 정보를 확인하기 위해 몇 가지 가정이 필요합니다. 분석기가 Type에서 작동하고 있으므로 제한된 지식으로 작업해야한다는 사실을 모두 아는 분석기라는 점에 감사하지만, 한 줄의 코드로 다음과 같이 다시 작성해야한다는 사실은 여전히 ​​실망 스럽습니다.

var genericAction = typeof(Action<>);

Contract.Assume(genericAction.IsGenericType);
Contract.Assume(genericAction.GetGenericArguments().Length == 1);

genericAction.MakeGenericType(typeof(Object));

문서화를 유지하기 위해 (예, ContractVerificationAttribute를 사용하여 메서드 / 클래스 등에 대해이 기능을 해제하거나 SuppressMessageAttribbute를 사용하여 특정 메시지를 대상으로 할 수 있지만 코드가 억제 등으로 빠르게 흩어지기 때문에 목적을 무너 뜨리는 것 같습니다.

또한 다음과 같은 경우를

  public class MyClass
    : IInterface
  {
    private readonly Object _obj;

    public Object Property
    {
      get
      {
        Contract.Ensures(Contract.Result<Object>() != null);
        return _obj;
      }
    }

    public MyClass(Object obj)
    {
      Contract.Requires(obj != null);

      _obj = obj;
    }
  }

obj는 null이 아니어야하며 변경할 수없는 readonly 필드로 설정되어 있지만 여전히 null을 반환하지 않는 속성 요구 사항을 증명할 수 있도록 클래스에 "markup"메서드를 추가해야합니다.

[ContractInvariantMethod]
private void ObjectInvariant()
{
  Contract.Invariant(_obj != null);
}

더 많은 것이 있지만 나는 아마도 충분히 헛소리를했을 것이라고 생각했고, 내가 코드 계약을 "좋아요"하고 코드 혼란스러운 느낌을 없애도록 도와 준 나보다 훨씬 똑똑한 사람들의 통찰력에 정말 감사 할 것이다. 더 나은 코드 구조, 문제 해결 방법 등에 대한 통찰력은 크게 감사하겠습니다.

감사!


이것은 저에게 그런 단서처럼 느껴집니다. 속성이나 내장 된 언어 지원을 통해 요구 사항을 문서화하는 더 깨끗한 방법이 있었으면합니다.

CC 팀은 Attributes를 사용하는 것만으로는 충분하지 않다고 말했습니다. 람다와 같은 것을 포함 할 수 없기 때문입니다. 그들은 같은 것을 포함 할 수[NotNull] 있지만 가능한 한 일반적으로 CC를 유지하려고하기 때문에 그렇게하지 않기로 결정했습니다 .

One reason that CC is a library (rather than part of an extended C#) is that it is supported across all .NET languages.

You can read more about the team's reasoning here.

In terms of actually using this, so far I've just been keeping my interface contracts in the same file as the interface, which means it's all documented in the same place. It is something that should be improved upon :)

Code Bloat [...]

Your second complaint is probably something that can be implemented -- I'd suggest posting it on the code contracts forum. (EDIT: Seems someone already has, but no answers yet.)

However, it will always be the case that under-specified contracts will need more assumptions surrounding them. If you run into a case like this in the .NET framework you can request that contracts be added in the Missing Contracts on Libraries thread.

In addition, taking a case like [...]

This has been addressed. If you have an auto-property, you just have to add an non-null invariant and the pre-/post-conditions will be generated:

public class MyClass : IInterface
{
    private Object Property { get; set; }

    [ContractInvariantMethod]
    private void Invariants()
    {
        Contract.Invariant(Property != null);
    }
}

You'll probably end up with other invariants for your classes anyway, so it's not that big a deal.


Yeah, contracts clutter things up, but I feel like it's worth it when PEX reads the code contracts and generates 25 unit tests and 100% code coverage for me. If you haven't used PEX yet, then you're missing out on the biggest benefit of code contracts. Here's a starter guide for using PEX with contracts.


I really wanted to like it too, and went quite far with an implementation in a new project, until I gave up under the weight of small niggling issues I had with it.

I would love for Microsoft to resurrect Spec#. I know code contracts was written as a library to be available across languages, but even just the speed of static analysis cries out for compiler support.

This guy raises some of the same points: http://earthli.com/news/view_article.php?id=2183

Anyway, there was no one problem that was a deal-breaker, but an accumulation of irritations:

  • Static analysis is very slow. You can set it to run in the background, but then it's very easy to ignore.
  • Static analysis isn't great, for example:
    • It only handles simple control flow
    • It won't assume that readonly fields are invariant
    • Collections are not handled by the static checker (only runtime)
  • No delegate contracts
  • Doesn't work well with Resharper unless you actively suppress resharper warnings one way or another
    • Resharper is optimistic. It will assume a method parameter is not null, until you give it a hint that it might be - like Contract.Assume(thing != null)
  • Can generate a lot of warnings and suggestions, sometimes stemming from an ambiguous source.
    • The moment any team member lets their attention slip, the number of warnings would become overwhelming for everybody
  • The need to specify abstract contract classes for interfaces is pain.
  • Runtime contracts use IL rewriting, which apparently doesn't play well with other rewriting solutions like PostSharp (I think .
    • As a result of IL rewriting, edit-and-continue can't be used
  • Contracts stick very tightly to liskov substitution principle: Subtypes can never relax pre-conditions. I mean, good in principle, but I imagine it would be a pain when working with over-contracted 3rd party code.

If your are concerned about code bloat then I would encourage you to look at Aspect Oriented Programming as an alternative.

This will allow you to specify the contract in the aspect and then wrap any classes or methods of classes in the contract. If your contract is going to be used in more than one class then this would be a cleaner way to implement this.

Unfortunately the support for AOP in C# is not great but there are a couple of libraries that add this functionality.

And as I also have the same feeling towards other C# implementations as you have found with Contracts it all seems fine at first until you lift the hood and start using it in a serious fashion.


I have created a plugin for Visual Studio 2010 which can save you some time creating code contracts for interfaces and abstract classes: http://visualstudiogallery.msdn.microsoft.com/en-us/d491911d-97f3-4cf6-87b0-6a2882120acf


Actually, I find the interface implementation great. It's not cluttering your interface or your regular code, and you can neatly store it in some folder in your solution, or in the same class file as your interface, whichever you like most.

Why are you required to add the object invariant? You are only required to add as much as you feel necessary.

I understand your point about code bloat, but I guess that's the trade-off with Code Contracts. Extra checks/security means extra codes, at least how it's currently implemented. I'd try to keep as much contracts as possible on the Interfaces, that should at least help you ease the code bloat pain.

참고URL : https://stackoverflow.com/questions/3077933/really-trying-to-like-codecontracts-in-c-sharp

반응형