0

Apologies if this would be a better fit for the software engineering stack exchange, but as this is fairly language-specific, I believe I'm asking in the right place.

I have a project which involves converting a sizable codebase from Java to C#. The original Java project has an interface roughly equivalent to the following C# snippet.

public interface IEntry<out T> where T : class, IEntry<T>{ T SetName(string nameIn); string GetName(); } 

It also contains a basic implementation of this interface.

public abstract class BaseEntry<T> : IEntry<T> where T : BaseEntry<T>{ private string name; internal object extraData; public T SetName(string nameIn){ name = nameIn; return (T)this; } public string GetName(){ return name; } } 

The problem arises in the class which holds entries of types implementing the IEntry<T> interface.

public class ObjectHolder<T> where T : class, IEntry<T>{ private readonly List<T> entries; private readonly bool canGetExtraData; public ObjectHolder(){ //HasGenericBaseType simply checks if the first type implements the second type canGetExtraData = HasGenericBaseType(typeof(T), typeof(BaseEntry<>)); } public void Add(T entry){ entries.Add(entry); } public void DoSomething(int index){ if(!canGetExtraData){ T entry = entries[index]; object extraData = ((BaseEntry<T>)entry).extraData; <-- This cast fails on compilation //Something gets done with the extraData object } } } 

The indicated line gives me the below error on compilation.

error CS0314: The type `T' cannot be used as type parameter `T' in the generic type or method `BaseEntry<T>'. There is no boxing or type parameter conversion from `T' to `BaseEntry<T>' 

I understand why I get this error, but I'm struggling to find a way around it. How could the above class be redesigned to allow access to the internal object field on types extending BaseEntry<T>?

6
  • Can you clarify why you can't use class restriction and instead perform it at run-time? (Obviously you've seen plenty of answers which show how to use (object) to get around error at casting time... so not closing as duplicate). Commented Jan 20, 2021 at 23:20
  • 1
    So BaseEntry<T> is not equal to IEntry<T> You have a mammal that implements ILegs. but not everything that has ILegs is a mammal. Is there any reason you cant push extraData back to interface? Also there is other really suspect stuff going on here, like HasGenericBaseType. unfortunately with this sort of question, a concrete answer is problematic as we have no idea what problems you are trying to solve Commented Jan 20, 2021 at 23:21
  • Declare it as T extraData { get; set; } on the interface? Incidentally this whole pattern you describe is very similar to Curiously Recurring Template Pattern Commented Jan 20, 2021 at 23:35
  • IMHO define some other internal interface IExtraData with no generic constraint, then you won't need to prove to the compiler that T : BaseEntry<T>. That simplifies canGetExtraData to if (entries[index] is IExtraData extra) ... extra.extraData; Commented Jan 21, 2021 at 0:08
  • Because the Java runtime doesn't actually implement generics, it is less strict about forcing constraints on edge cases like this. Commented Jan 21, 2021 at 0:10

1 Answer 1

1

So basically BaseEntry<T> is not equal to IEntry<T>

Analogy : You have a Mammal that implements ILegs, but not everything that has ILegs is a Mammal.

There are few fixes that you might want to look at, either pushing extraData back to IEntry<T> or as @JeremyLakeman suggested, creating a separate generic or / non generic interface for IExtraData and constrain by that. The answer would depend on whether IEntry<T> always has extra data.

Sign up to request clarification or add additional context in comments.

1 Comment

Just implemented this in my project using the extra interface idea, as I realized that some entries did not need the extra data. Thanks for the solid analogy, that really set me on the right track

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.