Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic type conversion

Tags:

.net

generics

I'm having some serious design problems due to generics issues. Perhaps someone has some suggestions.

EDIT: So, I know this is not usually done, but I've completely changed my example code, because I've realized that the original pseudo-code did not really explain my problem. The following code much more closely resembles the real example I am dealing with. I hope that my problem will be clearer from it. I apologize ahead that it is a bit lengthy, but from my experience, problems with generics usually show up when you try to build a more complex structure. So:

class Program
    {
        static void Main(string[] args)
        {
            IConnector<IService> connector = ConnectorBuilderFactory.NewBuilder<IService>("someEndpoint").MakeReliable().GetConnector();
            connector.Connect();
        }
    }

    public interface IService : IConnectionMaintainable
    {
        void DoSomething();
    }

    public interface IConnectionMaintainable
    {
        DateTime GetServerTime();
    }

    public interface IConnector<T>
    {
        T Channel { get; }
        void Connect();
        void Disconnect();
    }

    public interface IConnectorBuilder<T>
    {
        IConnector<T> GetConnector();
        IConnectorBuilder<T> MakeReliable();
        // ...more connector-configuration methods
    }

    public class ChannelWatchDog<T> where T : IConnectionMaintainable
    {
        private IConnector<T> connector;

        public ChannelWatchDog(IConnector<T> connector /*various other parameters*/)
        {
            this.connector = connector;
        }

        // ...methods that use connector's Connect, Disconnect, and GetServerTime methods
    }

    public class Connector<T> : IConnector<T>
    {
        private T channel;

        public Connector(string endpoint)
        {
            // ...build channel
        }

        public T Channel
        {
            get { return channel; }
        }

        public void Connect()
        {
            // ...connect to server
        }

        public void Disconnect()
        {
            // ...disconnect from server
        }
    }

    public class ConnectorBuilder<T> : IConnectorBuilder<T>
    {
        private string endpoint;

        public ConnectorBuilder(string endpoint)
        {
            this.endpoint = endpoint;
        }

        public IConnector<T> GetConnector()
        {
            Connector<T> connector = new Connector<T>(endpoint);

            // If reliability was requested, build the ChannelWatchDog: Following line does not compile:
            // ChannelWatchDog<T> watchDog = new ChannelWatchDog<T>(connector);

            return connector;
        }

        public IConnectorBuilder<T> MakeReliable()
        {
            // save various parameters required to build the ChannelWatchDog
            return this;
        }
    }

    public static class ConnectorBuilderFactory
    {
        public static IConnectorBuilder<T> NewBuilder<T>(string endpoint)
        {
            return new ConnectorBuilder<T>(endpoint);
        }
    }

So, firstly, if you find the GetConnector method in the ConnectorBuilder class, you will see the commented line of code, which does not compile if uncommented. This line is the essence of my problem. The problem might be obvious from the code, but I will try to explain it anyway in case it is not:

  1. I have an internal class (ChannelWatchDog) that needs an IConnector. But not just any IConnector, an IConnector, because other than the non-generic IConnector methods, it also needs the GetServerTime method from the IConnectionMaintainable interface.

  2. To simplify construction of connectors, I hoped to implement a builder using the Expression Builder pattern (the IConnectionBuilder interface). However, I want to be able to construct any IConnector, not just IConnector<IConnectionMaintainable>. Therefore, I cannot constrain T in IConnectorBuilder in the same way as I constrain it for the ChannelWatchDog. Lacking this constraint, I have no way to build it when GetConnector is called. Adding the constraint to the MakeReliable method does not help.

So, essentially the reason I posted this question was that I wanted to do something that is apparently impossible. I wanted the ChannelWatchDog and ConnectorBuilder class to look something like this:

public class ChannelWatchDog
    {
        private IConnector<IConnectionMaintainable> connector;

        public ChannelWatchDog(IConnector<IConnectionMaintainable> connector /*various other parameters*/)
        {
            this.connector = connector;
        }

        // ...methods that use connector's Connect, Disconnect, and GetServerTime methods
    }

    public class ConnectorBuilder<T> : IConnectorBuilder<T>
    {
        private string endpoint;

        public ConnectorBuilder(string endpoint)
        {
            this.endpoint = endpoint;
        }

        public IConnector<T> GetConnector()
        {
            Connector<T> connector = new Connector<T>(endpoint);

            // If reliability was requested, build the ChannelWatchDog: Following line does not compile:
            ChannelWatchDog watchDog = new ChannelWatchDog((IConnector<IConnectionMaintainable>)connector);

            return connector;
        }

        public IConnectorBuilder<TReliable> MakeReliable<TReliable>() where TReliable : T, IConnectionMaintainable
        {
            // save various parameters required to build the ChannelWatchDog
            return (IConnectorBuilder<TReliable>)this;
        }
    }

But the cast to IConnector fails at runtime.

So that was much more lengthy than I had originally intended. If you've gotten this far in reading then you already have my thanks :) Any ideas are welcome, including restructuring the code.

BTW, Having not found a solution to this myself, I created different ConnectorBuilders (in this case, a ReliableConnectorBuilder) and different factory methods in the factory. But I don't like this solution much.

EDIT: Just to clarify and reiterate: I cannot constrain the IConnector nor the ConnectionBuilder because these need to support cases in which the IConnectionMaintainable interface is not implemented.

like image 993
joniba Avatar asked Dec 01 '10 09:12

joniba


People also ask

What is generic type C#?

Generic is a class which allows the user to define classes and methods with the placeholder. Generics were added to version 2.0 of the C# language. The basic idea behind using Generic is to allow type (Integer, String, … etc and user-defined types) to be a parameter to methods, classes, and interfaces.

Is generic a type?

Definition: “A generic type is a generic class or interface that is parameterized over types.” Essentially, generic types allow you to write a general, generic class (or method) that works with different types, allowing for code re-use.

How does convert ChangeType work?

ChangeType(Object, TypeCode) is a general-purpose conversion method that converts the object specified by value to a predefined type specified by typeCode . The value parameter can be an object of any type.

What are generic type constraints?

A type constraint on a generic type parameter indicates a requirement that a type must fulfill in order to be accepted as a type argument for that type parameter. (For example, it might have to be a given class type or a subtype of that class type, or it might have to implement a given interface.)


1 Answers

Code to the interface?

GenericClass<IFoo> wrapper = new GenericClass<IFoo>(new FooImplementor());
Acceptor acceptor = new Acceptor(wrapper);
like image 68
Marc Gravell Avatar answered Sep 22 '22 14:09

Marc Gravell