views:

240

answers:

2

I am creating an explicit conversion operator to convert between a generic list of entity types to a generic list of model types. Does anyone know why I get the following error:

User-defined conversion must convert to or from the enclosing type

I already have an explicit conversion operator between Entity.objA and Model.objA which works fine. The problem arises when trying to convert the generic list. Is this even possible?

Here is my code:

    public static explicit operator List<Model.objA>(List<Entity.objA> entities)
    {
        List<Model.objA> objs= new List<Model.objA>();
        foreach (Entity.objA entity in entities)
        {
            objs.Add((Model.objA)entity);
        }
        return claims;
    }

Thanks for any help.

+1  A: 

Basically, you can't do this. In your operator either the input or output type must be of the type that declares the operator. One option might be an extension method, but to be honest LINQ Select comes pretty close by itself, as does List<T>.ConvertAll.

As an example of the extension method approach:

public static class MyListUtils {
  public static List<Model.objA> ToModel(this List<Entity.objA> entities)
  {
    return entities.ConvertAll(entity => (Model.objA)entity);
  }
}

or more generally with LINQ Select:

public static class MyListUtils {
  public static IEnumerable<Model.objA> ToModel(
     this IEnumerable<Entity.objA> entities)
  {
    return entities.Select(entity => (Model.objA)entity);
  }
}

and just use someList.ToModel().

Marc Gravell
A: 

The error "User-defined conversion must convert to or from the enclosing type" says exactly what it means. If you have a conversion operator

class MyClass {
    public static explicit operator xxx(string s) { // details }
    public static implicit operator string(xxx x) { // details }
}

Then xxx must be MyClass. This is what is meant by the "conversion must convert to or from the enclosing type." The enclosing type here is MyClass.

The relevant section of the ECMA334 C# spec is 17.9.4:

A conversion operator converts from a source type, indicated by the parameter type of the conversion operator, to a target type, indicated by the return type of the conversion operator. A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true, where S0 and T0 are the types that result from removing the trailing ? modifiers, if any, from S and T:

S0 and T0 are different types.

Either S0 or T0 is the class or struct type in which the operator declaration takes place.

Neither S0 nor T0 is an interface-type.

Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

So here's your code:

public static explicit operator List<Model.objA>(List<Entity.objA> entities) {
    List<Model.objA> objs= new List<Model.objA>();
    foreach (Entity.objA entity in entities) {
        objs.Add((Model.objA)entity);
    }
    return claims;
}

The issue is that for this to be defined as a conversion operator it must reside in the List<Model.objA> or List<Entity.objA> classes but of course you can not do that as you don't have access to change those types.

You could use Enumerable.Select to project to the other type, or List<T>.ConvertAll. For example:

public static class ListExtensions {
    public static List<Model.objA> ConvertToModel(this List<Entity.objA> entities) {
        return entities.ConvertAll(e => (Model.objA)e);
    }
}
Jason