3

I have such a structure

void Main()
{
    var lol = ActionClass.GetTestTuple<Request, Response, RequestCallInfo>("lol", "user");
    lol.Dump();
}

public class ActionClass
{
    public static Tuple<TRes, TInfo> GetTestTuple<TReq, TRes, TInfo>(string resultMsg, string userName)
    where TReq : BaseRequest, new()
    where TRes : BaseResponse, new()
    where TInfo : CallInfo<TReq>, new()
    {
        var response = new TRes { Message = resultMsg };
        var eventsInfo = new TInfo();
        eventsInfo.Data.UserName = userName;

        return new Tuple<TRes, TInfo>(response, eventsInfo);
    }
}

public class Request : BaseRequest
{
}

public class Response : BaseResponse
{
}

public class RequestCallInfo : CallInfo<Request>
{
    public string Item { get; set; }
}

public class CallInfo<GenericType> : BaseCallInfo where GenericType : BaseRequest, new()
{
    public GenericType Data { get; set; }

    public CallInfo(GenericType x)
    {
        Data = x;
    }
}

public class BaseCallInfo
{
    public string CallItem { get; set; }
}

public class BaseRequest
{
    public string UserName { get; set; }
}

public class BaseResponse
{
    public string Message { get; set; }
}

and when I execute it, I get UserQuery.CallInfo<UserQuery.Request> does not contain a constructor that takes 0 arguments,

So i tried this way

public static Tuple<TRes, TInfo> GetTestTuple<TReq, TRes, TInfo>(string resultMsg, string userName)
    where TReq : BaseRequest, new()
    where TRes : BaseResponse, new()
    where TInfo : CallInfo<TReq>, new()
{
    var response = new TRes { Message = resultMsg };
    var eventsInfo = new TInfo(new TReq { UserName = userName });
    eventsInfo.Data.UserName = userName;

    return new Tuple<TRes, TInfo>(response, eventsInfo);
}

but i'm getting 'TInfo': impossible to provide arguments when instantiating a type

How can I pass an instance of TReq to TInfo and keep only the parameter constructor in CallInfo<GenericType>?

1
  • OT: signature constraints are in the specs for c#7. i really hope they don't ditch it again. Commented Mar 23, 2016 at 17:30

2 Answers 2

1

The new() constraint only requires that a generic type have a public parameter-less constructor; there is no way to specify specific construct signatures on a generic type / constraint in C#. Given that, the compiler cannot know what constructors would be available by callers, so you can't instantiate a generic type using constructor parameters.

So you could use reflection here if you would like, but this solution seems simpler based on the code you've provided:

var eventsInfo = new TInfo() { Data  = new TReq { UserName = userName } };
Sign up to request clarification or add additional context in comments.

15 Comments

It would be so nice if we could declare constructors on Interfaces.
@MatthewWhited That can be easily worked-around with factories
@Phate01 then you violate the new() constraint that specifies that TInfo must have a parameterless constructor.
@icbytes That would compile , but since there's still no guarantee that the actual class has a constructor that matches the desired signature, it's susceptible to errors at runtime.
@icbytes CreateInstance does not do that automatically. You would have to look for a constructor that matches, and if one does not exist, then call the parameterless one. It's possible but messy.
|
0

You should use Activator.CreateInstance method.

Working solution:

public class ActionClass
{
    public static Tuple<TRes, TInfo> GetTestTuple<TReq, TRes, TInfo>(string resultMsg, string userName)
        where TReq : BaseRequest, new()
        where TRes : BaseResponse, new()
        where TInfo : CallInfo<TReq>
    {
        var response = new TRes { Message = resultMsg };
        var eventsInfo = (TInfo)Activator.CreateInstance(typeof(TInfo), new []{ new TReq() });
        eventsInfo.Data.UserName = userName;

        return new Tuple<TRes, TInfo>(response, eventsInfo);
    }
}

public class BaseCallInfo
{
    public string CallItem { get; set; }
}

public class BaseRequest
{
    public string UserName { get; set; }
}

public class BaseResponse
{
    public string Message { get; set; }
}

public class Request : BaseRequest
{
}

public class Response : BaseResponse
{

}

public class RequestCallInfo : CallInfo<Request>
{
    public string Item { get; set; }

    public RequestCallInfo(Request x) : base(x)
    {

    }
}

public class CallInfo<GenericType> : BaseCallInfo where GenericType : BaseRequest, new()
{
    public GenericType Data { get; set; }

    public CallInfo(GenericType x)
    {
        Data = x;
    }
}

}

1 Comment

As several people have commented on the other answer, this will fail at runtime for any class that does not have a matching constructor signature. While this will work given the specific code the OP provided, this is a very bad practice (IMO) as it presents potential bugs to another implementer of the CallInfo<TReq> in the future who isn't aware of this code.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.