Suppose you have an existing large project and you want to integrate Code Contracts in it. The existing code uses if-null-then-throw logic. For the given conditions, the documentation suggests to set the assembly mode to Custom Argument Validation.
I have the following classes:
class A
{
protected virtual void Foo(int a, int b)
{
if (a == null)
throw new ArgumentNullException(a);
if (b == null)
throw new ArgumentNullException(b);
Contract.EndContractBlock();
}
}
class B : A
{
protected override void Foo (int a, int b)
{
// some stuff
base.Foo(a, b);
}
}
When I compile I get the following warning:
warning CC1055: Method 'B.Foo(int, int)' should contain custom
argument validation for 'Requires(a != null)'
as it overrides 'A.Foo(int,int)' which suggests it does. If you don't
want to use custom argument validation in this assembly, change the
assembly mode to 'Standard Contract Requires'.
I don't want to repeat the preconditions on every overridden method! Is there a way around it?
It works fine if you use Contract.Requires() instead of Contract.EndContractBlock().
There is a section in the manual quoted below which suggests adding a [SuppressMessage] attribute to the method override.
From the Code Contracts user manual p.22 section 5.2.3.
Delegating Checks to Other Methods
Suppose you have a code pattern similar to the following code:
public class Base {
public virtual void Compute(string data) {
if (data == null) throw new ArgumentNullException(...);
Contract.EndContractBlock();
...
}
}
public class Derived : Base {
public override void Compute(string data) {
base.Compute(data);
...
}
}
Then the tools will issue warning CC1055 with a message of the form:
Method ’Derived.Compute’ should contain custom argument validation for
' Requires (ArgumentNullException)(data ! = null)' as it overrides
'Base.Compute' which suggests it does.
In this situation, the warning is not helpful, as the implementation
of Derived.Compute delegates the parameter validation to another
method (in this case the base method). To avoid the warning in this
situation without repeating the validation, you can add a
SuppressMessage attribute to the method:
public class Derived : Base {
[SuppressMessage("Microsoft.Contracts", "CC1055", Justification = "Validation performed in base method")]
public override void Compute(string data) {
base.Compute(data);
...
}
}
Related
I would like to control in a class the signature of certain methods so that it throws a compilation error if a method does not have the signature I want.
I have thought do it creating a custom attribute, something like this:
public class CustomAttributeForMethodWithStringParameterFirstAttribute : Attribute
{
}
public class MyClass
{
[CustomAttributeForMethodWithStringParameterFirst]
public void Method1 (string p)
{
}
[CustomAttributeForMethodWithStringParameterFirst]
public void Method2 () // <--- I wish a compile error
{
}
}
But... Now I do not know how to continue.
Is it possible do this via attributes?
Thanks in advance.
If not, is there any way to do it?
EDIT
Sorry, I have simplified my scenario to expone the concrete problem.
I'm writting the interface for a WCF service and I wish implement this attributes in the interface. Certain methods must have one string parameters in first position for custom operations.
I've developed a custom attribute to do something with this first parameter before methods calls of my service.
public interface MyInterface
{
[CustomAttributeForMethodWithStringParameterFirst]
void Method1 (string p);
[CustomAttributeForMethodWithStringParameterFirst]
void Method2 (); // <--- I wish a compile error
}
I have a custom attribute now I didn't know that how to restrict the access to the method on which I have applied my attribute.
For example: I have custom attribute say "CustomRole" now if value of CustomRole is "Admin" then and only then it should access method.
CustomRole["Admin"]
public void Method()
{
// code
}
How to perform verify the value?
You need some kind of aspect oriented programming approach here. Attribute itself can't 'access' the method as it is not evaluated by the runtime, but you might use some framework which will intercept the call, check the attribute and context and properly handle the case.
So in short, you will:
decorate a method with an attribute
provide an interceptor to handle the call
instantiate the class via some tools which provides AOP functionality
execute the call. The call will be intercepted and handled according to your implementation
Specifying the requirements
As you already noted, this can be easily specified with attributes:
[RequiredPermission(Permissions.CanCreateOrder)]
public virtual Guid CreateOrder(string orderCode) {...}
Intercepting the call
Now you need to pick a tool which will instantiate your object and intercept calls to it. This can be done with IoC containers which support AOP, or you can wrap it manually (e.g. use an AOP tool to create a proxy to the object and use the proxy).
You need to write an interceptor, or a wrapper method which has a chance to evaluate the call context before forwarding the execution to your method or rejecting the call.
You can find a discussion and code samples here. Take a look at OrderManagementService class which declares requirements via attribute.
Poor man's AOP
You can do all of this without resorting to proper AOP tools, but in a less generic way (which may be perfectly fine for simpler projects), using some form of Decorator pattern - please note, this is written from head, not in IDE:
interface IService
{
void Method();
}
class ServiceImpl : IService // one of many implementations
{
[CustomRole("Admin")]
public void Method() { ... }
}
class ServiceChecker : IService // one of many implementations
{
IService m_svc;
public ServiceChecker(IService wrapped) { m_svc = wrapped; }
public void Method()
{
var mi = m_svc.GetType().GetMethod("Method");
if(mi.IsDefined(typeof(CustomRoleAttribute), true)
{
CustomRoleAttribute attr = (CustomRoleAttribute)mi.GetCustomAttributes(typeof(CustomRoleAttribute), true)[0];
if(!attr.Role.Equals( GetCurrentUserRole() ) // depends on where you get user data from
{
throw new SecurityException("Access denied");
}
}
m_svc.Method();
}
}
// the client code
IService svc = new ServiceChecker(new ServiceImpl());
svc.Method();
Your code looks a little bit wrong.
Here is my class with the method with a CustomRoleAttribute
public class MyClass
{
[CustomRole("Admin")]
public void MyMethod()
{
}
}
You attribute should define the AttributeUsage to ensure other developers not uses your attribute on a property or constructor:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false,Inherited = true)]
public class CustomRoleAttribute : Attribute
{
public string Role { get; private set; }
public CustomRoleAttribute(string role)
{
Role = role;
}
}
And now put both together:
MyClass myClass = new MyClass();
MethodInfo[] methods = myClass.GetType().GetMethods(); // Access all the public methods.
foreach (var methodInfo in methods) // iterate trough all methods.
{
// Get all custom attributes from the method.
object[] attributes = methodInfo.GetCustomAttributes(typeof (CustomRoleAttribute), true);
if (attributes.Length > 0)
{
CustomRoleAttribute attribute = (CustomRoleAttribute)attributes[0];
if (attribute.Role == "Admin")
{
// the role is admin
}
}
}
You see now, how to use attributes. Bu you must first check the attribute and than access the method.
i have generic method. i want generic method to limit one type. problem is derived type not to be allowed - i do not want this. example codes:
public static T Generate<T>(T input)
where T : Operation // ALLOWS BinaryOperation - NOT WANT THIS
{
//...
}
how to do what I request?
problem is derived type not to be allowed
There is no way to enforce this constraint, without checking it at runtime. Doing so would be a violation of the Liskov Substitution Principle, which states that any type should allow you to pass in a derived type without restriction.
If you must enforce this, it will only work with a runtime check, like:
public static T Generate<T>(T input)
where T : Operation // ALLOWS BinaryOperation - NOT WANT THIS
{
// Checks to see if it is "Operation" (and not derived type)
if (input.GetType() != typeof(Operation))
{
// Handle bad case here...
}
// Alternatively, if you only want to not allow "BinaryOperation", you can do:
if (input is BinaryOperation)
{
// Handle "bad" case of a BinaryOperation passed in here...
}
}
Note that, in this case, there's really no reason to make it generic, as the same code would work as:
public static Operation Generate(Operation input)
{ // ...
It's not possible to force a method to accept only one specific type like Operation if the type is not a struct or a sealed class.
Let's me show this in a example why this won't work anyway:
public void Generate<T>(Operation op)
// We assume that there is the keyword "force" to allow only Operation classes
// to be passed
where T : force Operation
{ ... }
public void DoSomething()
{
Generate(new BitOperation()); // Will not build
// "GetOperation" retrieves a Operation class, but at this point you dont
// know if its "Operation" or not
Operation op = GetOperation();
Generate(op); // Will pass
}
public Operation GetOperation() { return new BitOperation(); }
As you can see it will be easy to pass a BitOperation even when there is a restriction.
Solution
There is only one solution beside the others mentioned above ( struct, sealed ): Runtime Check.
You can write yourself a little helper method for this.
public class RuntimeHelper
{
public static void CheckType<T>(this Object #this)
{
if (typeof(T) != #this.GetType())
throw new ....;
}
}
Usage
public void Generate(Operation op)
{
op.CheckType<Operation>(); // Throws an error when BitOperation is passed
}
Little Note
If you want to speed up the helper you could use a generic class RuntimeHelper<T> with a static readonly type variable which has the type of T.
When you're doing this you cannot longer use extension methods so the call would look like this:
RuntimeHelper<Operation>.CheckType(op);
https://github.com/apache/log4net
I am compiling log4net from the source above, but it doesn't pass verification:
[IL]: Error: [log4net.dll : log4net.Plugin.RemoteLoggingServerPlugin::Attach][offset 0x00000029] Method is not visible.
Code is ok:
public interface ILoggerRepository
{
...
}
public interface IPlugin
{
void Attach(ILoggerRepository repository);
}
public abstract class PluginSkeleton : IPlugin
{
public virtual void Attach(ILoggerRepository repository) { }
}
public class RemoteLoggingServerPlugin : PluginSkeleton
{
override public void Attach(ILoggerRepository repository)
{
base.Attach(repository);
...
}
}
https://github.com/apache/log4net/blob/trunk/src/Plugin/IPlugin.cs
https://github.com/apache/log4net/blob/trunk/src/Plugin/PluginSkeleton.cs
https://github.com/apache/log4net/blob/trunk/src/Plugin/RemoteLoggingServerPlugin.cs
Investigation shows that it fails in calling RemotingServices.Marshal():
override public void Attach(ILoggerRepository repository)
{
base.Attach(repository);
// Create the sink and marshal it
m_sink = new RemoteLoggingSinkImpl(repository);
try
{
**RemotingServices.Marshal(m_sink, m_sinkUri, typeof(IRemoteLoggingSink));**
}
catch(Exception ex)
{
LogLog.Error(declaringType, "Failed to Marshal remoting sink", ex);
}
}
But there is nothing crucial here. Moreover calling RemotingServices.Marshal() with any type leads to the same problems:
Even if I change the Attach() to this:
override public void Attach(ILoggerRepository repository)
{
RemotingServices.Marshal(null, null, typeof(int));
}
Can someone spot what is the problem?
The problem is related to the fact that with .NET 4 Level 2 transparency was introduced. (See http://msdn.microsoft.com/en-us/library/dd233102.aspx for details.)
The method override public void Attach(ILoggerRepository repository) is lacking the SecuritySafeCriticalAttribute. Adding the attribute:
#if NET_4_0
[System.Security.SecuritySafeCritical]
#endif
override public void Attach(ILoggerRepository repository)
{
// ...
}
will make the IL verification pass. (Also see: http://msdn.microsoft.com/en-us/library/bb397858.aspx for further information.)
Update: To shed some more light on why verification fails (which might not be immediately clear by just reading the articles in the links provided) here is a short explanation.
RemotingServices.Marshal has the [SecuritySafeCritical] attribute applied. So one would assume that calling the method from a transparent method would be allowed. However RemotingServices.Marshal returns an object of type System.Runtime.Remoting.ObjRef and said type is annotated with the [SecurityCritical] attribute. If the log4net code would store a reference to the returned value in a local variable, Code Analysis would detect the error and issue a CA2140 warning ("Transparent code must not reference security critical items").
Now apparently under the security transparency rules, a transparent method may not call a security safe-critical method if the called method returns a security critical type even if the transparent method does not store a reference to the returned object as the following sample demonstrates:
public class TransparencyRulesDemo
{
[SecuritySafeCritical]
public void SafeGetCritical()
{
GetCritical();
}
public void TransparentGetCritical()
{
// Below line will trigger a CA2140 warning if uncommented...
// var critical = GetCritical();
// ...the following line on the other hand will not produce any warning
// but will lead to IL verification errors and MethodAccessExceptions if
// called from transparent code.
GetCritical();
}
[SecuritySafeCritical]
public Critical GetCritical()
{
return new Critical();
}
}
[SecurityCritical]
public class Critical
{
}
This btw. makes the [SecuritySafeCritical] attribute on RemotingServices.Marshal kind of pointless.
If I annotate a method of a class with an attribute, I can then retrieve custom attributes for that class and see if it has that attribute or not. For example, I'm building a message-oriented program so I have classes like
public class ErrorHandler
{
[HandleMessage(ErrorHandling.ERROR)]
private static void OnError(string message, object context, params object[] args)
{
Exception e;
args.Extract(out e);
Console.WriteLine(e.Message + Environment.NewLine + e.StackTrace);
}
}
At runtime I can do this:
public static void RegisterStaticHandlers(Type type)
{
foreach (var mInfo in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
{
var mAttr = mInfo.GetCustomAttribute<HandleMessageAttribute>();
if (mAttr != null)
RegisterInstanceHandler(mAttr.Message, mInfo.CreateDelegate<MessageHandler>());
}
}
(I have some extension methods to simplify the code, they're not relevant now.)
My question is: can I get rid of this RegisterStaticHandlers method altogether and register the handler in the attribute constructor?
public class HandleMessageAttribute : Attribute
{
public string Message { get; private set; }
public HandleMessageAttribute(string message)
{
Message = message;
Messages.RegisterInstanceHandler(message, ... method reference here ...);
}
}
Is there a way to retrieve the method that is annotated by an attribute in the attribute constructor, instead of the opposite (and regular) way of having the method and getting its attributes?
[Edited]
I just realized that I can at least do this in the static constructor:
static ErrorHandler()
{
Messages.RegisterStaticHandlers(typeof(ErrorHandler));
}
This at least keeps the registration of a class inside that class, which is great as far as I'm concerned :)
In short, you can't do it from the attribute constructor, When the attribute is constructed it has no access to the target method/class/field/etc to which it is being applied.
From the code, I'm assuming you want the code to auto-register on startup? One way to do this might be to probe your assembly (once, at startup) - enumerate all classes (foreach(Type type in assembly)), finding those with candidate error handlers. This reduces the maintenance burden. Another option would be something similar using a configuration file.