Getting behavior of Java's Class<? extends Map> in .NET

前端 未结 3 2005
一个人的身影
一个人的身影 2021-01-16 21:04

I have a generic class in java defined as:

public static class KeyCountMap 
{
   private Map map = new LinkedHashMap

        
相关标签:
3条回答
  • 2021-01-16 21:41

    Maybe this is what you want?

    public class KeyCountMap<T>
        where T : new()
    {
        private Dictionary<T, MutableInt> map = new Dictionary<T, MutableInt>();
        // ... rest of properties...  
    
        public KeyCountMap()
        { }
    
        public KeyCountMap(T obj)
        {
            obj = new T();
            map = (Dictionary<T, MutableInt>)(object)obj;
        }
    }
    
    0 讨论(0)
  • 2021-01-16 21:49

    I assume you know Java erases any generic type information after compiling (there's metadata for variables, but actual objects are void of generic type information). Moreover, your code is not type safe:

    @SuppressWarnings({ "unchecked", "rawtypes" })
    

    You're using this because you're creating a non-parameterized instance of Map.

    In .NET, you don't get around the type system like this, because generic type information is kept and used at runtime.

    Let's see your C# code:

    public static class KeyCountMap<T>  
    

    A static class in C# is a class that cannot be instanced, it's used for its static members alone. I think you don't want this. Perhaps KeyCountMap is a static nested class in Java, as opposed to an inner class.

    In C#, you don't have inner classes. Nested classes don't share data with an instance of the containing class, it's as if the name of the containing class is part of the namespace for the nested class. So, you don't need, and actually don't want, the static keyword here.

    {  
       private Dictionary<T, MutableInt> map = new Dictionary<T, MutableInt>();
    

    In .NET, Dictionary is a class. To keep the intent, you should use IDictionary, the corresponding interface, as the type for the map field.

       // ... rest of properties...  
    
       public KeyCountMap()  
       { }  
    
       public void KeyCountMap<T>(T obj) where T : Dictionary<T, MutableInt>  
    

    Why the void return type, isn't this a constructor?

    In C#, constructors can't be generic. You probably want a Type.

    Your C# code just doesn't make sense, so here's what you could do:

       public KeyCountMap(Type dictionaryType)
       {
          if (!typeof(IDictionary<T, MutableInt>).IsAssignableFrom(dictionaryType))
          {
              throw new ArgumentException("Type must be a IDictionary<T, MutableInt>", nameof(dictionaryType));
          }
          map = (IDictionary<T, MutableInt>)Activator.CreateInstance(dictionaryType);
       }
    }  
    

    We're checking the type before creating an instance. If we didn't, we would create an instance, the cast would fail and the assignment wouldn't even happen, so the new instance would just be garbage.

    It may be that the actual instance will be a proxy; if so, you may not want to check the type before creating an instance.


    You can't just copy-paste Java as C# (or vice-versa) and expect to make just a few changes until it works, for some definition of works, e.g. it compiles. The languages are not that similar, and chances are that too many subtle things are wrong.

    This approach might be fun at first, but you'll stumble so often it will soon stop being any fun at all. You should learn the basics and understand the way things are done in the target language before you start translating code line-by-line. Many times, you may find that something you had to do in one environment already exists in the other or vice-versa, or that something may take more or less steps to do in the other, etc.

    In this particular case, Java made Class be a generic class, while .NET kept Type a non-generic class. In .NET only interfaces and delegates may state generic type covariance or contravariance. This is rather restrictive anyway, if Type was generic, the intended uses could be either covariant or contravariant. But remember that in Java, a generic Class<T> at runtime is as good as Class, it only has any value at compile time and you can tell the compiler you know better anyway, just like you did.

    0 讨论(0)
  • 2021-01-16 21:55

    There are two problems. First, you need to tell the compiler that T has a parameterless constructor, so you can call new T(). You can do that by providing the new() argument to the class definition.

    You also have to tell the compiler that T is actually the dictionary you are trying to assign, so we have to extend the class a little more:

    public class KeyCountMap<K>
    {
        private Dictionary<K, MutableInt> map = new Dictionary<K, MutableInt>();
        // ... rest of properties...
    

    Note that K is the key type of the dictionary, which you didn't specify yet.

    Second, the T in your method can be another T than in your class. Omitting that will do the trick:

    public void Map()
    {
        var obj = new Dictionary<K, MutableInt>();  // Unable to define new instance of T
        map = obj;      // Unable to convert T to base class
    }
    
    0 讨论(0)
提交回复
热议问题