drl
drl

Reputation: 841

Pass C# generic type as a parameter

I have an interface declared as

public interface ISomething<T> where T : class

and somewhere in the interface I have a member declared as

[JsonProperty("someProperty")]
[JsonConverter(typeof(ConcreteTypeConverter<List<T>>))]
List<T> SomePropertyList{ get; set; }

I get an error on ConcreteTypeConverter<List<T>> saying it cannot use a type (T) as an argument. My ConcreteTypeConverter class takes a type T and returns a concrete implementation of T which is needed for JSON deserialization.The scenario here is that T can have about 20 different types.But I would like to avoid having 20 such interfaces - that's why I opted for a generic interface.

Usage would be something like

ISomething<SomeType> variable = new Something<SomeType>();
var list = variable.SomePropertyList;

where SomeType is the actual implementation of the T. Is there any way to use generics in such a situation?

My ConcreteConverterClass derives from JsonConverter (using Newtonsoft.Json):

public class ConcreteTypeConverter<TConcrete> : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return true;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return serializer.Deserialize<TConcrete>(reader);
        }
    }

My exact error is:

Attribute Argument cannot use type parameters

Upvotes: 3

Views: 2106

Answers (4)

Artem Bakhmat
Artem Bakhmat

Reputation: 307

As it was told before, the generic parameters cannot appear in attribute declarations, I encountered the same problem and previous suggestions were not for me, I needed one SingleValueToCollectionConverter which could work like generic. And reflection helped me:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)   
{
   foreach (var prop in objectType.GetProperties())
   {
      var type = prop.PropertyType;

      if (!type.IsClass)
          continue;

      var destination = Activator.CreateInstance(objectType);
      var result = reader.TokenType == JsonToken.StartArray 
            ? serializer.Deserialize(reader, objectType) 
            : new List<object> { serializer.Deserialize(reader, type) };

      return Mapper.Map(result, destination, result.GetType(), destination.GetType());
   }

      return null;
 }

And using like this (If Data will be Object in json, it will be converted to list):

public class BaseResponse<TData>
{
    [JsonConverter(typeof(SingleValueToCollectionConverter))]
    public List<TData> Data { get; set; }
}

I hope it can help somebody

Upvotes: 0

AgentFire
AgentFire

Reputation: 9790

Quoting:

[JsonConverter(typeof(ConcreteTypeConverter<List<T>>))]
List<T> SomePropertyList{ get; set; }

I get an error on ConcreteTypeConverter

Since the generic parameters cannot appear in attribute declarations, there is an advice for you from me:

public class ConcreteTypeConverter : JsonConverter

Removed the TConcrete. Since the ReadJson returns an object, the power of generics simply fades out.

Second,

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    return serializer.Deserialize<object>(reader);
}

Replaced TConcrete with an object. If that is not enough for your code to work, try to workaround it. Your method returns an object anyway.

Upvotes: 1

Patrick McDonald
Patrick McDonald

Reputation: 65451

You only need the ConcreteTypeConverter if SomePropertyList is not a concrete type, e.g.

[JsonProperty("someProperty")]
[JsonConverter(typeof(ConcreteTypeConverter<List<T>>))]
IList<T> SomePropertyList{ get; set; }

If this is not an issue, just change your property declaration to

[JsonProperty("someProperty")]
List<T> SomePropertyList{ get; set; }

Upvotes: 4

Amit Bagga
Amit Bagga

Reputation: 648

Please apply the attribute on the class implementing the interface.

[JsonProperty("someProperty")]
[JsonConverter(typeof(ConcreteTypeConverter<List<T>>))]

Upvotes: 0

Related Questions