Robert Koritnik
Robert Koritnik

Reputation: 105069

Providing concrete type values for generic method parameters

I have a generic interface that defines functionality to secure particular value types. Particular provider implementations are able to protect some value type values (i.e. ISecurityProvider<int>) or strings (ISecurityProvider<string>). That's why I put a generic type constraint to IConvertible to cover both.

Provider interface definition

public interface ISecurityProvider<TSecurable>
    where TSecurable : IConvertible // to constrain to struct types AND string
{
    TSecurable Secure(TSecurable value);
}

Then I have my entity classes that should be able to use these providers and secure their own properties based on security provider's implementation.

I define an abstract base entity class that actual ones inherit from

public abstract class BaseEntity
{
    protected bool IsSecured { get; set; }

    protected virtual void SecureSelf<TSecurable>(ISecurityProvider<TSecurable> provider)
        where TSecurable : IConvertible
    {
        if (!this.IsSecured)
        {
            this.IsSecured = true;
        }
    }
}

Example entity with identity property that has to be secured.

public class SomeDataRecord : BaseEntity
{
    public int Id { get; set; }

    public string Name { get; set; }

    public override void SecureSelf<TSecurable>(ISecurityProvider<TSecurable> provider)
    {
        if (!this.IsSecured)
        {
            base.SecureSelf(provider);
            this.Id = provider.Secure(this.Id); // COMPILER ERROR
        }
    }
}

The error I'm getting is:

The best overloaded method match for 'ISecurityProvider.Secure(TSecurable)' has some invalid arguments.

How should I call provider.Secure() method to make it work?

Additional information

Basically I would like my individual entity classes to secure all applicable properties which may be of different types. As in my example where I have an integer and a string property.

My SecureSelf method should secure all of them at the same time as they're all relying on the same IsSecured property. So my example is not really the best one. I should likely rather provide a security provider factory to my SecureSelf method and internally it should get particular provider instances to secure indiviual types of properties. Or so I think...

Upvotes: 0

Views: 2438

Answers (2)

Gusman
Gusman

Reputation: 15161

You are getting that exception because "int" is not "TSecurable", you can avoid this making BaseEntity generic and in your specific class inherit from the base with a concrete type:

public interface ISecurityProvider<TSecurable>
where TSecurable : IConvertible // to constrain to struct types AND string
{
    TSecurable Secure(TSecurable value);
}

public abstract class BaseEntity<TSecurable> where TSecurable : IConvertible
{
    protected bool IsSecured { get; set; }

    protected virtual void SecureSelf(ISecurityProvider<TSecurable> provider)
    {
        if (!this.IsSecured)
        {
            this.IsSecured = true;
        }
    }
}

public class SomeDataRecord : BaseEntity<int>
{
    public int Id { get; set; }

    public string Name { get; set; }

    protected override void SecureSelf(ISecurityProvider<int> provider)
    {
        if (!this.IsSecured)
        {
            base.SecureSelf(provider);
            this.Id = provider.Secure(this.Id); // COMPILER ERROR
        }
    }

}

Also, if everything will be IConvertible you can use a non-generic approach:

public interface ISecurityProvider
{
    IConvertible Secure(IConvertible value);
}

public abstract class BaseEntity
{
    protected bool IsSecured { get; set; }

    protected virtual void SecureSelf(ISecurityProvider provider)
    {
        if (!this.IsSecured)
        {
            this.IsSecured = true;
        }
    }
}

public class SomeDataRecord : BaseEntity
{
    public int Id { get; set; }

    public string Name { get; set; }

    protected override void SecureSelf(ISecurityProvider provider)
    {
        if (!this.IsSecured)
        {
            base.SecureSelf(provider);
            this.Id = (int)provider.Secure(this.Id);
            this.Name = (string)provider.Secure(this.Name);
        }
    }
}

Upvotes: 0

Lee
Lee

Reputation: 144206

Given the way you're trying to use Secure you should put the constraint on the method instead of on the interface:

public interface ISecurityProvider
{
    TSecurable Secure<TSecurable>(TSecurable value) where TSecurable : IConvertible;
}

public abstract class BaseEntity
{
    protected bool IsSecured { get; set; }

    protected virtual void SecureSelf(ISecurityProvider provider)
    {
        if (!this.IsSecured)
        {
            this.IsSecured = true;
        }
    }
}

public class SomeDataRecord : BaseEntity
{
    public int Id { get; set; }

    public string Name { get; set; }

    protected override void SecureSelf(ISecurityProvider provider)
    {
        if (!this.IsSecured)
        {
            base.SecureSelf(provider);
            this.Id = provider.Secure(this.Id);
        }
    }
}

Upvotes: 2

Related Questions