C
C#2y ago
Hugh

✅ Creating a generic type based on a Type variable rather than a specific type

I've got the following code:
public class QueryResponse<T>
{
T? Data { get; set; }
bool Success { get; set; } = false;
string? ErrorMessage { get; set; }
}
public class QueryResponse<T>
{
T? Data { get; set; }
bool Success { get; set; } = false;
string? ErrorMessage { get; set; }
}
I then want to take this class, and create a Type object that is a QueryResponse<> with the generic type parameter being based on another Type. So something like this. This doesn't work, though as dtoType is a variable, and not a type.
public QueryResponse<T>? GetSomething<T>()
{
Type dtoType = new T().GetDTOType(); // This will be a different type, but one that a T can be created from
Type receiveType = typeof(QueryResponse<dtoType>);
QueryResponse<dtoType>? result = (QueryResponse<dtoType>)JsonSerializer.Deserialize(content, receiveType);
if(result is null)
{
return null;
}
return new QueryResponse<T>()
{
Data = new T(result.Data),
Success = true
};
}
public QueryResponse<T>? GetSomething<T>()
{
Type dtoType = new T().GetDTOType(); // This will be a different type, but one that a T can be created from
Type receiveType = typeof(QueryResponse<dtoType>);
QueryResponse<dtoType>? result = (QueryResponse<dtoType>)JsonSerializer.Deserialize(content, receiveType);
if(result is null)
{
return null;
}
return new QueryResponse<T>()
{
Data = new T(result.Data),
Success = true
};
}
Is this possible? My overall goal here is to have DTO types and Internal types, and be able to pass the internal types to a function which will automatically convert them to their DTO types (this bit works fine), and then when the function receives the DTO result, have that result converted back to the expected return type and returned. I think that this would be fine, in general, but my problem is that I'm wrapping the return result in the QueryResponse
13 Replies
Hugh
HughOP2y ago
Alternatively, does anyone have any suggestions for how else I could approach this? I'd like to deal with the Internal -> DTO and DTO -> Internal conversions in this function This is my main function definition:
protected async Task<QueryResponse<ReturnType>> RequestPostResult<ContentType, ReturnType>(string host, int port, string route, ContentType content)
where ReturnType : MyStructBase, new()
where ContentType : MyStructBase
protected async Task<QueryResponse<ReturnType>> RequestPostResult<ContentType, ReturnType>(string host, int port, string route, ContentType content)
where ReturnType : MyStructBase, new()
where ContentType : MyStructBase
Where MyStructBase is an abstract base class:
public abstract class MyStructBase
{
public abstract Type GetDTOType();
}
public abstract class MyStructBase
{
public abstract Type GetDTOType();
}
🎉 perfect - thanks - that sounds like it's exactly what I need here hmm - not sure I want to be going too far down that route, TBH Maybe I should just accept that I should be doing the DTO conversion at the calling end... I'll see whether I can do it without, and if not, I may go a different route Is there any alternate approach that you'd suggest for this same thing? Hmm - I guess I could do:
public abstract class MyStructBase<DTOType>
{
public DTOType ToDTO()
{
return new DTOType(this);
}
}

public class MyDataDTO
{
public MyDataDTO(MyData obj)
{}
}

public class MyData : MyStructBase<MyDataDTO>
{
public MyData(MyDataDTO obj)
{}
}
public abstract class MyStructBase<DTOType>
{
public DTOType ToDTO()
{
return new DTOType(this);
}
}

public class MyDataDTO
{
public MyDataDTO(MyData obj)
{}
}

public class MyData : MyStructBase<MyDataDTO>
{
public MyData(MyDataDTO obj)
{}
}
What I'm not sure which this is whether I could define a FromDTO() static function on MyStructBase that would allow be to create a MyData object from a MyDataDTO while only knowing the type MyData Is that what you were meaning? Also, not sure how I can tell it that the DTOType needs to have a constructor that takes the current type... I can say:
public abstract class SWStructBase<DTOType> where DTOType : new()
{
public DTOType ToDTO()
{
return new DTOType(this);
}
}
public abstract class SWStructBase<DTOType> where DTOType : new()
{
public DTOType ToDTO()
{
return new DTOType(this);
}
}
But this doesn't specify what arguments the constructor can take In that function, it will let me return new DTOType() but not new DTOType(this) I think so I shall have a read yeah you know what, I'm going to roll back to something similar to what I had before Where the conversion to/from DTO has to be done at the caller end But I can use a where to ensure that the types being passed in are DTO types, which will make it less error-prone
ero
ero2y ago
you gotta stop violating c# convention, it makes my heart hurt also using reflection in a web api feels very very wrong...
Hugh
HughOP2y ago
I’m pretty new to C# so am trying to get my head around what the convention is - I’m very open to learning!
ero
ero2y ago
generic type parameters are always TName never NameType
Hugh
HughOP2y ago
👍
ero
ero2y ago
so TDto, TReturn, TContent
Hugh
HughOP2y ago
Ever just T?
ero
ero2y ago
a lot of the time you add something to be more explicit about what it is
Hugh
HughOP2y ago
Yup
ero
ero2y ago
generally when you add a constraint
Hugh
HughOP2y ago
I’ve been using T a lot and just making it more specific when I’ve got more than 1
ero
ero2y ago
but Linq for example always uses IEnumerable<TSource>
Hugh
HughOP2y ago
I’ve changed all of them in this project to TContent/TReturn I appreciate your patience! yeah, that makes sense
Want results from more Discord servers?
Add your server