Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

generic function with a "has property X" constraint?

Tags:

I have a third-party, closed source application that exports a COM interface, which I am using in my C#.NET application through Interop. This COM interface exports many objects that all show up as System.Object until I cast them to the appropriate interface type. I want to assign an property of all of these objects. Thus:

foreach (object x in BigComInterface.Chickens)
{
    (x as Chicken).attribute = value;
}
foreach (object x in BigComInterface.Ducks)
{
    (x as Duck).attribute = value;
}

But assigning the property is likely (for application-specific reasons that are unavoidable) to throw Exceptions from which I want to recover, so I really want a try/catch around each one. Thus:

foreach (object x in BigComInterface.Chickens)
{
    try
    {
        (x as Chicken).attribute = value;
    }
    catch (Exception ex)
    {
        // handle...
    }
}
foreach (object x in BigComInterface.Ducks)
{
    try
    {
        (x as Duck).attribute = value;
    }
    catch (Exception ex)
    {
        // handle...
    }
}

Obviously, it would be so much cleaner to do this:

foreach (object x in BigComInterface.Chickens)
{
    SetAttribute<Chicken>(x as Chicken, value);
}
foreach (object x in BigComInterface.Ducks)
{
    SetAttribute<Duck>(x as Duck, value);
}

void SetAttribute<T>(T x, System.Object value)
{
    try
    {
        x.attribute = value;
    }
    catch
    {
        // handle...
    }
}

See the problem? My x value can be of any type, so the compiler can't resolve .attribute. Chicken and Duck are not in any kind of inheritance tree and they do not share an interface that has .attribute. If they did, I could put a constraint for that interface on T. But since the class is closed-source, that's not possible for me.

What I want, in my fantasy, is something like a constraint requiring the argument to have the .attribute property regardless of whether it implements a given interface. To wit,

void SetAttribute<T>(T x, System.Object value) where T:hasproperty(attribute)

I'm not sure what to do from here other than to cut/paste this little try/catch block for each of Chicken, Duck, Cow, Sheep, and so on.

My question is: What is a good workaround for this problem of wanting to invoke a specific property on an object when the interface that implements that property cannot be known at compile time?

like image 404
catfood Avatar asked Aug 20 '09 17:08

catfood


People also ask

What are the constraints in generics?

The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.

Can generic classes be constrained?

You can constrain the generic type by interface, thereby allowing only classes that implement that interface or classes that inherit from classes that implement the interface as the type parameter. The code below constrains a class to an interface.

How do you add a generic constraint?

You can specify one or more constraints on the generic type using the where clause after the generic type name. The following example demonstrates a generic class with a constraint to reference types when instantiating the generic class.

What is generic with example in C#?

Generic Class T is called type parameter, which can be used as a type of fields, properties, method parameters, return types, and delegates in the DataStore class. For example, Data is generic property because we have used a type parameter T as its type instead of the specific data type.


2 Answers

Well, depending on how humongous your exception handling code is (and if i am not mistaken it could be quite so) using the following trick might help you:

class Chicken
{
    public string attribute { get; set; }
}

class Duck
{
    public string attribute { get; set; }
}

interface IHasAttribute
{
    string attribute { get; set; }
}

class ChickenWrapper : IHasAttribute
{
    private Chicken chick = null;
    public string attribute
    {
        get { return chick.attribute; }
        set { chick.attribute = value; }
    }
    public ChickenWrapper(object chick)
    {
        this.chick = chick as Chicken;
    }
}

class DuckWrapper : IHasAttribute
{
    private Duck duck = null;
    public string attribute
    {
        get { return duck.attribute; }
        set { duck.attribute = value; }
    }
    public DuckWrapper(object duck)
    {
        this.duck = duck as Duck;
    }
}

void SetAttribute<T>(T x, string value) where T : IHasAttribute
{
    try
    {
        x.attribute = value;
    }
    catch
    {
        // handle...
    }
}
like image 154
gha.st Avatar answered Oct 10 '22 11:10

gha.st


Unfortunately, this is tricky currently. In C# 4, the dynamic type may help quite a bit with this. COM interop is one of the places that dynamic really shines.

However, in the meantime, the only option that allows you to have any type of object, with no restrictions on interfaces, would be to revert to using reflection.

You can use reflection to find the "attribute" property, and set it's value at runtime.

like image 31
Reed Copsey Avatar answered Oct 10 '22 11:10

Reed Copsey