0

I have a problem with generic types in C#. This is my minimal client:

using Castle.DynamicProxy;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;

namespace WebProxyClientTester
{


    public class LiveResultPromise<R, L>
    {
        private Task<R> result;
        private IObservable<L> notification;

        public LiveResultPromise(Task<R> result, IObservable<L> notification)
        {
            this.result = result;
            this.notification = notification;
        }

        public Task<R> Result { get => result; set => result = value; }
        public IObservable<L> Notification { get => notification; set => notification = value; }
    }

    public class UserContact
    {
        public UserContact()
        {

        }
    }

    public class User
    {
        public User()
        {

        }
    }

    public class AddressBook
    {
        public AddressBook()
        {

        }
    }

    class Response<T>
    {
        private int id;
        private T result;
        private object error;

        public int Id { get => id; set => id = value; }
        public T Result { get => result; set => result = value; }
        public object Error { get => error; set => error = value; }
    }

    public interface MyInterface
    {
        LiveResultPromise<UserContact, UserContact> getUserContact(String username);
        LiveResultPromise<User, User> getUsers();
        LiveResultPromise<AddressBook, AddressBook> getAddressBook();
    }


    class Client
    {
        Subject<Response<dynamic>> wsResponse = new Subject<Response<dynamic>>();
        int id = 1;
        public Client()
        {

        }

        public Subject<Response<dynamic>> WsResponse { get => wsResponse; set => wsResponse = value; }

        public dynamic Invoke(String methodName, object[] arguments, Type returnType)
        {
            TaskCompletionSource<dynamic> taskResult = new TaskCompletionSource<dynamic>();
            IObservable<Response<dynamic>> notification = Observable.Create<Response<dynamic>>((result) =>
            {
                wsResponse.Subscribe((res) =>
                {
                    if (id == res.Result)
                    {
                        result.OnNext(res.Result);
                    }

                }, (error) => { });

                return Disposable.Create(() => Console.WriteLine("Observer has unsubscribed"));
            });

            LiveResultPromise<dynamic, dynamic> liveResultPromise = new LiveResultPromise<dynamic, dynamic>(taskResult.Task, notification);
            id++;
            return liveResultPromise;
        }
    }

    class ProxyUtils : IInterceptor
    {
        private Client client;
        public ProxyUtils(Client client)
        {
            this.client = client;
        }

        public void Intercept(IInvocation invocation)
        {
            invocation.ReturnValue = client.Invoke(invocation.Method.Name, invocation.Arguments, invocation.Method.ReturnType);
        }
    }

    class TestCLientExample
    {
        private static MyInterface requestClient;
        static void Main(string[] args)
        {
            Client client = new Client();
            requestClient = new ProxyGenerator().CreateInterfaceProxyWithoutTarget<MyInterface>(new ProxyUtils(client));
            LiveResultPromise<User, User> users = requestClient.getUsers();
            LiveResultPromise<UserContact, UserContact> contact = requestClient.getUserContact("pippo");
            LiveResultPromise<AddressBook, AddressBook> addressBook = requestClient.getAddressBook();

            users.Notification.Subscribe((result) =>
            {
                Console.WriteLine("User Object");
            });

            contact.Notification.Subscribe((result) =>
            {
                Console.WriteLine("UserContact Object");
            });

            addressBook.Notification.Subscribe((result) =>
            {
                Console.WriteLine("AddressBook Object");
            });

            Response<User> res1 = new Response<User>();
            res1.Id = 1;
            res1.Result = new User();

            client.WsResponse.OnNext(res1);

            Response<UserContact> res2 = new Response<UserContact>();
            res2.Id = 2;
            res2.Result = new UserContact();

            client.WsResponse.OnNext(res2);

            Response<AddressBook> res3 = new Response<AddressBook>();
            res3.Id = 3;
            res3.Result = new AddressBook();

            client.WsResponse.OnNext(res3);
        }
    }
}

I have two problems whit my code, first in this part

 Response<User> res1 = new Response<User>();
 res1.Id = 1;
 res1.Result = new User();

 client.WsResponse.OnNext(res1);

beacuse client.WsResponse want Response<dynamic> but i put Response<User> and compiler fails with error: can't convert Response<User> to Response<dynamic>.

I can resolve with this part of code:

Response<dynamic> res1 = new Response<dynamic>();
res1.Id = 1;
res1.Result = new User();

client.WsResponse.OnNext(res1);

Response<dynamic> res2 = new Response<dynamic>();
res2.Id = 2;
res2.Result = new UserContact();

client.WsResponse.OnNext(res2);

Response<dynamic> res3 = new Response<dynamic>();
res3.Id = 3;
res3.Result = new AddressBook();

client.WsResponse.OnNext(res3);

second problem is the result:

LiveResultPromise<User, User> users = requestClient.getUsers();
LiveResultPromise<UserContact, UserContact> contact = requestClient.getUserContact("pippo");
LiveResultPromise<AddressBook, AddressBook> addressBook = requestClient.getAddressBook();

because for example LiveResultPromise<dynamic, dynamic> can't cast to LiveResultPromise<User, User>

How I can do something similar?

Thank you.

kuskmen
  • 3,648
  • 4
  • 27
  • 54
Mattia
  • 133
  • 2
  • 8
  • 3
    @HimBromBeere I don't think that's a good duplicate. OP wants to change the type that is created. However, I think the question is either XY or needs to be refactored to not require this at all. – DavidG Jul 25 '18 at 14:03
  • Could you make `Invoke` generic as well instead of passing in the type? Or is the type created dynamically (meaning you don't know it at compile-time) – D Stanley Jul 25 '18 at 14:04
  • @DavidG It is a duplicate if the solution is "use reflection to construct the method call" but that is an assumption and I agree that there may be a better solution. – D Stanley Jul 25 '18 at 14:04
  • Also, why are you using `Invoke` at all? You're not using the `methodName` and `argument` parameters, so I don't see why you need tho wrap the call in `Invoke` – D Stanley Jul 25 '18 at 14:07
  • @D Stanley i have updated my post with invocation class, i don't know type at compiled-time. – Mattia Jul 25 '18 at 14:19
  • If you don´t know the type at compile-time it´s impossible to get the type at compile-time. That simple. However it´s pretty unclear what your method should actually do. The usual way to do this is by having a common interface that all your classes to be returned from `Invoke` implement. – MakePeaceGreatAgain Jul 25 '18 at 14:21
  • i have update my post and i hope now is more clear then before. I use `Castle.DynamicProxy` to call interface's methods. This interceptor call my `Invoke` method that convert requst as json request and return result object. I don't print this conversion because i think isn't important for my problem. If you need to know more, i can i print all my code. – Mattia Jul 25 '18 at 15:11
  • Not really, there are still many missing links between your code-fragments. I suggest you provide a [Minimal, Complete, and Verifiable example)(https://stackoverflow.com/help/mcve) before we guess any further. For example it´s unclear how that `WebAPIInterface` is related to your `client`-instance. And in particular you still don´t provide how you call that `Intercept`-method. – MakePeaceGreatAgain Jul 25 '18 at 15:30
  • I have update my post with minimal client and i triedto explain my problems. I hope now is more clear. Tell me if i can give you more informations, unfortunately the project is big and not simple to minimalize. – Mattia Jul 26 '18 at 07:57

1 Answers1

0

i've resolved my problem with this solution

using Castle.DynamicProxy;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;

namespace WebProxyClientTester
{
   public class LiveResultPromise<R, L>
    {
        private Task<R> result;
        private IObservable<L> notification;

        public LiveResultPromise(Task<R> result, IObservable<L> notification)
        {
            this.result = result;
            this.notification = notification;
        }

        public Task<R> Result { get => result; set => result = value; }
        public IObservable<L> Notification { get => notification; set => notification = value; }
    }

    public class UserContact
    {
        public UserContact()
        {

        }
    }

    public class User
    {
        public User()
        {

        }
    }

    public class AddressBook
    {
        public AddressBook()
        {

        }
    }

    class Response
    {
        private int id;
        private dynamic result;
        private object error;

        public int Id { get => id; set => id = value; }
        public dynamic Result { get => result; set => result = value; }
        public object Error { get => error; set => error = value; }
    }

    class MutableInt
    {
        private int value;

        public MutableInt(int value)
        {
            this.Value = value;
        }

        public int Value { get => value; set => this.value = value; }

        public int GetAndIncrement()
        {
            int last = Value;
            Value++;
            return last;
        }
    }

    public interface MyInterface
    {
        LiveResultPromise<UserContact, UserContact> getUserContact(String username);
        LiveResultPromise<User, User> getUsers();
        LiveResultPromise<AddressBook, AddressBook> getAddressBook();
    }


    class Client
    {
        Subject<Response> wsResponse = new Subject<Response>();
        MutableInt idRequest = new MutableInt(1);
        public Client()
        {

        }

        public Subject<Response> WsResponse { get => wsResponse; set => wsResponse = value; }

        public dynamic Invoke<T, R>(String methodName, object[] arguments, Type returnType, T typeResult, R typeNotify)
        {
            int currentId = idRequest.GetAndIncrement();
            TaskCompletionSource<T> taskResult = new TaskCompletionSource<T>();
            IObservable<R> notification = Observable.Create<R>((result) =>
            {
                wsResponse.Subscribe((res) =>
                {
                    if (currentId == res.Id)
                    {                      
                        result.OnNext(res.Result);
                    }

                }, (error) => { });

                return Disposable.Create(() => Console.WriteLine("Observer has unsubscribed"));
            });

            LiveResultPromise<T, R> liveResultPromise = new LiveResultPromise<T, R>(taskResult.Task, notification);            
            return liveResultPromise;
        }
    }

    class ProxyUtils : IInterceptor
    {
        private Client client;
        public ProxyUtils(Client client)
        {
            this.client = client;
        }

        public void Intercept(IInvocation invocation)
        {
            System.Type genericType = invocation.Method.ReturnType;
            dynamic typeResult = null;
            dynamic typeNotify = null;

            if (genericType.GetGenericArguments().Length > 0)
            {
                if (genericType.GetGenericArguments().Length >= 1)
                {
                    typeResult = genericType.GetGenericArguments()[0];
                }

                if (genericType.GetGenericArguments().Length >= 2)
                {
                    typeNotify = genericType.GetGenericArguments()[1];
                }
            }

            var result = Activator.CreateInstance(typeResult);
            var notification = Activator.CreateInstance(typeNotify);

            invocation.ReturnValue = client.Invoke(invocation.Method.Name, invocation.Arguments, invocation.Method.ReturnType, result, notification);
        }
    }

    class TestCLientExample
    {
        private static MyInterface requestClient;
        static void Main(string[] args)
        {
            Client client = new Client();
            requestClient = new ProxyGenerator().CreateInterfaceProxyWithoutTarget<MyInterface>(new ProxyUtils(client));
            LiveResultPromise<User, User> users = requestClient.getUsers();
            LiveResultPromise<UserContact, UserContact> contact = requestClient.getUserContact("pippo");
            LiveResultPromise<AddressBook, AddressBook> addressBook = requestClient.getAddressBook();

            users.Notification.Subscribe((result) =>
            {
                Console.WriteLine("User Object");
            });

            contact.Notification.Subscribe((result) =>
            {
                Console.WriteLine("UserContact Object");
            });

            addressBook.Notification.Subscribe((result) =>
            {
                Console.WriteLine("AddressBook Object");
            });

            Response res1 = new Response();
            res1.Id = 1;
            res1.Result = new User();

            client.WsResponse.OnNext(res1);

            Response res2 = new Response();
            res2.Id = 2;
            res2.Result = new UserContact();

            client.WsResponse.OnNext(res2);

            Response res3 = new Response();
            res3.Id = 3;
            res3.Result = new AddressBook();

            client.WsResponse.OnNext(res3);
        }
    }
}

I have a doubt about this part because I'm afraid I can create a memory leak.

 var result = Activator.CreateInstance(typeResult);
 var notification = Activator.CreateInstance(typeNotify);

say me if you know better solution for this.

Thank You.

Mattia
  • 133
  • 2
  • 8