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

3

0

I have a generic class in java defined as:

public static class KeyCountMap<T> 
{
   private Map<T, MutableInt> map = new LinkedHashMap<T, MutableInt>();
   // ... rest of the properties...  

   public KeyCountMap()  
   { }  

   @SuppressWarnings({ "unchecked", "rawtypes" })  
   public KeyCountMap(Class<? extends Map> mapType) throws InstantiationException, IllegalAccessException   
   {
      map = mapType.newInstance();
   }  
   //... rest of the methods...  
}  

I have defined same class in .NET as:

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

   public KeyCountMap()  
   { }  

   public void KeyCountMap<T>(T obj) where T : Dictionary<T, MutableInt>  
   {  
      obj = new T();  // Unable to define new instance of T
      map = obj;      // Unable to convert T to base class
   }  
}  

And then a method is defined to sort map of type KeyCountMap<T> by value in the descending order . The method is defined as:

public static KeyCountMap<T> SortMapByDescendValue<T>(KeyCountMap<T> _map)
{
   List<KeyValuePair<T, MutableInt>> _list = new List<KeyValuePair<T, MutableInt>>(_map.EntrySet());  
   // whereas _map.EntrySet() return of type HashSet<KeyValuePair<T, MutableInt>>  
   _list = _list.OrderByDescending(_x => _x.Value).ToList();

   KeyCountMap<T> _result = new KeyCountMap<T>();
   foreach (KeyValuePair<T, MutableInt> _entry in _list)
   {
       _result.Put(_entry.Key, _entry.Value);
   }
   return _result; 
}

How can I get corrected the class defined in .NET ?

Hagi answered 14/6, 2016 at 7:53 Comment(5)
What is the purpose of this? Note that hash tables/maps/dictionaries don't guarantee any order. Are you transliterating Java to C#? If so, why?Yee
Yeah I'm transliterating for the sake of my project requirementsHagi
@Yee I have commented to your answer (accepted), please have a lookHagi
@Yee I'm here at your chat invitation. Are you there?Hagi
@Yee you were not in chat room in which you invited? When will you be there?Hagi
Y
1

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.

Yee answered 14/6, 2016 at 10:52 Comment(16)
@acelent--thanks for so long description, you guessed right that KeyCountMap is a nested class in Java, so its also a nested class in my C# code.Hagi
The definition of your constructor nameof() doesn't exist in current contextHagi
"The definition of your constructor nameof() doesn't exist in current context" -- That's C# 6.0, replace it with "dictionaryType" in older versions.Yee
Yeah I replaced it with dictionaryType.ToString(); insteadHagi
"Yeah I replaced it with dictionaryType.ToString(); instead" -- It's supposed to be the parameter's name, so please don't do that.Yee
But actually it should be a string and it is via .ToString() too, anyways I replaced back to "dictionaryType"Hagi
What about Mehrdad's answer, is that what is intended here?Hagi
"But actually it should be a string and it is via .ToString() too, anyways I replaced back to "dictionaryType"" -- Just because it is a string doesn't mean it's right. The purpose of using the parameter's name is so that when you see an ArgumentException (or derived), you can check which argument caused the exception.Yee
Yeah its more absoluteHagi
and what about the SortMapByValueDescend() method, will it work with this definition for class KeyCountMap<T> ?Hagi
"and what about the SortMapByValueDescend() method, will it work with this definition for class KeyCountMap<T>" -- You changed your question significantly. It was about translating some Java with generics to C#, now it includes that method. Can you move that part to a separate question?Yee
Ok I'll try moving that part to a separate questionHagi
@acelent--Now I have a new question about second partHagi
@acelent--I have used your constructor definition but it has never been used in the code, I debugged the control never goes to this constructor, map ever remains 0, why is it so?Hagi
@Taufel, I don't know either. Is this open-source? Can you share it? Is the constructor with a type argument ever called from your code or from some library you're using? If not, do you actually need it or is it some artifact to satisfy some Java library? Have you translated the rest of the class, e.g. the propertied and methods you mention in the Java class? Are they populating the dictionary? Are they actually called? What are you trying to achieve, really?Yee
@acelent--yeah I've translated the rest of the class, actually it is a complete project which I've translated. Most of the code is working but due to such type (map initialization etc.) of bugs, some issues are there. What to share? Complete code? We may have a discussion (chat) then if complete project is being shared.Hagi
V
1

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
}
Ventage answered 14/6, 2016 at 7:56 Comment(14)
By doing this, would I be able to get same functionality as in Java class defined above ?Hagi
I am not exactly sure that the Java class does, but this will at least fix your bugs.Ventage
You removed T obj from method parameters, so how to use obj = new T(); in method definition instead ?Hagi
You don't have to since T is already the type you need, right?Ventage
I have to have a new instance of T then assign it to the property mapHagi
But the class KeyCountMap<T> should be defined as same because I have some methods in my code which returns KeyCountMap<Int32> then how this class definition holds true?Hagi
Can you take a look at the simplified version?Ventage
You changed the method name to Map is this fine ?Hagi
if we don't use void keyword still it'll be fine instead ?Hagi
If you want to use it as your constructor, you should omit void, yes.Ventage
It looks like that this is only simple initialization of map inside Map() whereas it has been already instantiated while declaring aboveHagi
Whereas the motto of asking question is to get same functionality in .NET class as of Java's class defined aboveHagi
Yes. The previous code assumed you had a generic type deriving from Dictionary<K, MutableInt>, but you wanted to just pass in a key type. That is what it is now. :)Ventage
Ignoring my .NET version of class KeyCountMap<T> can you post yours complete .NET version of class KeyCountMap<T> following the Java version?Hagi
Y
1

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.

Yee answered 14/6, 2016 at 10:52 Comment(16)
@acelent--thanks for so long description, you guessed right that KeyCountMap is a nested class in Java, so its also a nested class in my C# code.Hagi
The definition of your constructor nameof() doesn't exist in current contextHagi
"The definition of your constructor nameof() doesn't exist in current context" -- That's C# 6.0, replace it with "dictionaryType" in older versions.Yee
Yeah I replaced it with dictionaryType.ToString(); insteadHagi
"Yeah I replaced it with dictionaryType.ToString(); instead" -- It's supposed to be the parameter's name, so please don't do that.Yee
But actually it should be a string and it is via .ToString() too, anyways I replaced back to "dictionaryType"Hagi
What about Mehrdad's answer, is that what is intended here?Hagi
"But actually it should be a string and it is via .ToString() too, anyways I replaced back to "dictionaryType"" -- Just because it is a string doesn't mean it's right. The purpose of using the parameter's name is so that when you see an ArgumentException (or derived), you can check which argument caused the exception.Yee
Yeah its more absoluteHagi
and what about the SortMapByValueDescend() method, will it work with this definition for class KeyCountMap<T> ?Hagi
"and what about the SortMapByValueDescend() method, will it work with this definition for class KeyCountMap<T>" -- You changed your question significantly. It was about translating some Java with generics to C#, now it includes that method. Can you move that part to a separate question?Yee
Ok I'll try moving that part to a separate questionHagi
@acelent--Now I have a new question about second partHagi
@acelent--I have used your constructor definition but it has never been used in the code, I debugged the control never goes to this constructor, map ever remains 0, why is it so?Hagi
@Taufel, I don't know either. Is this open-source? Can you share it? Is the constructor with a type argument ever called from your code or from some library you're using? If not, do you actually need it or is it some artifact to satisfy some Java library? Have you translated the rest of the class, e.g. the propertied and methods you mention in the Java class? Are they populating the dictionary? Are they actually called? What are you trying to achieve, really?Yee
@acelent--yeah I've translated the rest of the class, actually it is a complete project which I've translated. Most of the code is working but due to such type (map initialization etc.) of bugs, some issues are there. What to share? Complete code? We may have a discussion (chat) then if complete project is being shared.Hagi
I
0

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;
    }
}
Ignaz answered 14/6, 2016 at 10:19 Comment(8)
Is this same as done in Java version of this class ?Hagi
and what about declaring class as static as in Java ?Hagi
@Taufel: What are you trying to achieve by declaring it as static? I don't think it does what you think it does...Ignaz
I referred the static declared as in Java classHagi
@Taufel: Yes, and that's not answering my question.Ignaz
@Taufel: There is literally nothing for you to gain here by declaring it as static in C#, and you lose the ability to instantiate it as well.Ignaz
@Mehrdad--as I updated a static method using KeyCountMap<T> please have a look at that, is it fine then?Hagi
@Taufel: You're not even explaining what the problem is in your question, you're just asking me to translate your code. I'm not going to, I don't have time. Put some work into making your question crystal clear instead of making us guess every step of the way and then editing the question after we guess.Ignaz

© 2022 - 2024 — McMap. All rights reserved.