Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit cast fails for myClass<T> when T is IEnumerable<U>

Tags:

c#

.net

.net-3.5

I know and I think I understand about the (co/conta)variance issue with IEnumerables. However I thought the following code would not be affected by it.

[DataContract]
public class WcfResult<T>
{
    public WcfResult(T result)
    {
        Result = result;
    }

    public WcfResult(Exception error)
    {
        Error = error;
    }

    public static implicit operator WcfResult<T>(T rhs)
    {
        return new WcfResult<T>(rhs);
    }

    [DataMember]
    public T Result { get; set; }
    [DataMember]
    public Exception Error { get; set; }
}

This class is to mimic BackgroundWorker's RunWorkerCompletedEventArgs so I can return errors from my WCF service without faulting the connection.

Most of my code is working fine so I can do things like this

public WcfResult<Client> AddOrUpdateClient(Client client)
{
    try
    {
        //AddOrUpdateClient returns "Client"
        return client.AddOrUpdateClient(LdapHelper);
    }
    catch (Exception e)
    {
        return new WcfResult<Client>(e);
    }
}

and it works fine, however the folowing code gives a error

public WcfResult<IEnumerable<Client>> GetClients(ClientSearcher clientSearcher)
{
    try
    {
        //GetClients returns "IEnumerable<Client>"
        return Client.GetClients(clientSearcher, LdapHelper, 100);
    }
    catch (Exception e)
    {
        return new WcfResult<IEnumerable<Client>>(e);
    }
}

Where the error is

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<myNs.Client>'
to 'myNs.WcfResult<System.Collections.Generic.IEnumerable<myNs.Client>>'. An explicit
conversion exists (are you missing a cast?)

What is going wrong that is causing this error to happen?

like image 461
Scott Chamberlain Avatar asked Sep 10 '12 01:09

Scott Chamberlain


1 Answers

Ah, you've been foiled by a less-than-obvious restriction of the C# Language Specification.

For a given source type S and target type T, if S or T are nullable types, let S0 and T0 refer to their underlying types, otherwise S0 and T0 are equal to S and T respectively. 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:

· 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.

Now, it doesn't seem like this would apply because your implicit conversion function takes a generic parameter, but this restriction seems to apply to types used as generic arguments just as much. I took your example and changed IEnumerable to List (a full type, not just an interface) and it compiled without error.

To make a long story short, you're simply going to need to wrap any expression that returns an interface type in the WcfResult constructor, because the implicit cast won't be available for it.

return new WcfResult<IEnumerable<Client>>(Client.GetClients(clientSearcher, LdapHelper, 100));
like image 185
Adam Maras Avatar answered Oct 31 '22 18:10

Adam Maras