Принудительный тип параметра атрибута - PullRequest
4 голосов
/ 27 мая 2011

Есть ли способ обеспечить, чтобы параметр типа, передаваемый атрибуту, реализовывал определенный интерфейс?

public interface IExpectedInterface
{
    void InterfaceMethod();
}

public class MyCustomAttribute : Attribute
{
    public MyCustomAttribute(Type classType)
    {
        this.ConfirmAssignedClassType();

        _classType = classType;
    }

    public void SomeMethod<T>() where T : IExpectedInterface, new()
    {
        //var expectedType = Activator.CreateInstance(this._classType) as IExpectedInterface;
        var expectedType = Activator.CreateInstance(typeof(T)) as IExpectedInterface;

        if (expectedType == null)
        {
            // Wrong type

            throw new ArgumentException(string.Format("Wrong type: {0} could not be created or converted to IActionAuthorization", _classType.ToString()));
        }

        // Do something with expectedType

        expectedType.InterfaceMethod();
    }

    private void ConfirmAssignedClassType()
    {
        if (!typeof(IExpectedInterface).IsAssignableFrom(_classType))
        {
            // Wrong type
            // Can we enforce it via language construct

            throw new ArgumentException(string.Format("Wrong type: {0} must implement IExpectedInterface", _classType.ToString()));
        }

        if (this._classType.GetConstructor(Type.EmptyTypes) == null)
        {
            // Wrong type
            // Can we enforce it via language construct

            throw new ArgumentException(string.Format("Wrong type: {0} must have parameter less constructor", _classType.ToString()));
        }
    }

    private Type _classType;
}

public class TestClass
{
    [MyCustom(typeof(TestClassImplementsExpectedInterface))]
    public void TestMethod1()
    {
    }

    [MyCustom(typeof(TestClassDoesntImplementExpectedInterface))]
    public void TestMethod2()
    {
    }
}

public class TestClassImplementsExpectedInterface : IExpectedInterface
{
    public void InterfaceMethod()
    {
        return;
    }
}

public class TestClassDoesntImplementExpectedInterface
{
}

Ответы [ 4 ]

5 голосов
/ 27 мая 2011

Разве это не может быть сделано с генериками? (Отредактировано - не может создать общий подкласс атрибута)

   public class MyAttribute: Attribute 
    {
        private Type _ClassType;
        public MyAttribute(Type classType)
        {
            _ClassType = classType;
        }
        public void SomeMethod<T>() where T: IMyInterface
        {
            var expectedType = Activator.CreateInstance(typeof(T)) as IMyInterface;
        // Do something with expectedType
        }
    }

И, конечно, перевод другого ответа на использование "нового" имеет большой смысл!

3 голосов
/ 27 мая 2011

Если по какой-то причине вы не хотите связываться с обобщениями (например, помещать все атрибуты в список или что-то в этом роде), есть способ, который можно считать несколько лучшим, хотя без проверки во время компиляции. Вы можете вызвать метод Type.IsAssignableFrom вместо вызова Activator.CreateInstance (который может или не может работать в зависимости от того, есть ли ctor с 0 параметрами) (хотя кажется, что вы предполагаете, что есть ctor с 0 параметрами), так что это может быть несколько спорным).

if (!typeof(SomeInterface).IsAssignableFrom(_ClassType)))
// Throw exception
2 голосов
/ 27 мая 2011

Вы можете преобразовать свой метод в общий, например,

public class MyAttribute : Attribute
{
    public void SomeMethod<T>() where T : ISomeInterface, new()
    {
        var expectedType = new T();
        // Do something with expectedType
    }
}

Ограничение типа new() означает, что тип должен иметь открытый конструктор без параметров, что означает, что вы можете сделать new T(). Это устраняет необходимость использования Activator.

Обратите внимание, что, как указывает Дункан Хоу, универсальный тип не может наследоваться от Attribute, поэтому вы не можете сделать public class MyAttribute<T> : Attribute

0 голосов
/ 27 мая 2011

Почему вы не просто передаете объект через конструктор (например, Public MyAttribute(IMyInterface myObject)) и сохраняете его в свойстве.Тогда, если вам нужен тип, явно вызовите typeof(myObjectProperty).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...