C
C#4w ago
Tofaa

Help using generics and maps

I've been trying to make a basic listener consumer system for a Minecraft server implementation im making in c# from scratch. I come from a java background so c# is fairly similar but im stuck on trying to make a generic map of an unknown type at runtime.
private final Map<Class<? extends ClientPacket>, PacketConsumer>[] listeners = new Map[5];

public <T extends ClientPacket> void setListener(@NotNull ConnectionState state, @NotNull Class<T> packetClass, @NotNull PacketConsumer<T> consumer) {
this.listeners[state.ordinal()].put(packetClass, consumer);
}
@FunctionalInterface
public interface PacketConsumer<T extends ClientPacket> {
void consume(T packet, PlayerConnection connection);
}
private final Map<Class<? extends ClientPacket>, PacketConsumer>[] listeners = new Map[5];

public <T extends ClientPacket> void setListener(@NotNull ConnectionState state, @NotNull Class<T> packetClass, @NotNull PacketConsumer<T> consumer) {
this.listeners[state.ordinal()].put(packetClass, consumer);
}
@FunctionalInterface
public interface PacketConsumer<T extends ClientPacket> {
void consume(T packet, PlayerConnection connection);
}
I'm trying to rewrite this to c#. I have
private readonly ConcurrentDictionary<Type, Action<IClientPacket, Connection>>[] _listeners = [];
public void SetListener<T>(ConnectionState state, Type type, Action<T, Connection> listener) where T : IClientPacket
{
var dict = _listeners[(int)state];
dict[type] = listener; // cannot set this because its not IClientPacket, but rather T
}
private readonly ConcurrentDictionary<Type, Action<IClientPacket, Connection>>[] _listeners = [];
public void SetListener<T>(ConnectionState state, Type type, Action<T, Connection> listener) where T : IClientPacket
{
var dict = _listeners[(int)state];
dict[type] = listener; // cannot set this because its not IClientPacket, but rather T
}
Any help would be fantastic, thank you :D
19 Replies
Tofaa
Tofaa4w ago
Please ping me if anyone replies
Angius
Angius4w ago
Can't you just use the interface instead of a generic parameter even in the listener? Thing with Java is that generics are fake and don't exist during runtime, they just get replaced with an Object and a bunch of casts C# does not erase generics, so it's a bit more strict Stuff like type covariance/contravariance come into play
Tofaa
Tofaa4w ago
I could but then i'd need to define a class extending it at all times since Functional interfaces dont exist and c# has deligates instead Most of the listeners i need are one/two liners so it'd be a lot of unnecessary boilerplate But if that's my only alternative ig
Angius
Angius4w ago
Worst case scenario, use object and validate that it's of that interface ¯\_(ツ)_/¯
Tofaa
Tofaa4w ago
wdym
MODiX
MODiX4w ago
Angius
REPL Result: Success
interface IFoo { }
class Foo : IFoo { }

object f = new Foo();

if (f is IFoo ifoo)
{
Console.WriteLine("It's an IFoo");
}
interface IFoo { }
class Foo : IFoo { }

object f = new Foo();

if (f is IFoo ifoo)
{
Console.WriteLine("It's an IFoo");
}
Console Output
It's an IFoo
It's an IFoo
Compile: 473.945ms | Execution: 39.387ms | React with ❌ to remove this embed.
Angius
Angius4w ago
You can use pattern matching to check that an object is an IFoo and cast it to an IFoo in one go This is more like what Java would do under the hood
Tofaa
Tofaa4w ago
yeah but im not sure how that would help my case
Tofaa
Tofaa4w ago
you cant also put unknown generics into c# collections either so i couldnt convert it to an interface
No description
Angius
Angius4w ago
private readonly ConcurrentDictionary<Type, Action<object, Connection>>[] _listeners = [];
public void SetListener(ConnectionState state, Type type, Action<object, Connection> listener)
{
var dict = _listeners[(int)state];
dict[type] = listener;
}
private readonly ConcurrentDictionary<Type, Action<object, Connection>>[] _listeners = [];
public void SetListener(ConnectionState state, Type type, Action<object, Connection> listener)
{
var dict = _listeners[(int)state];
dict[type] = listener;
}
Tofaa
Tofaa4w ago
unless u mean i make it store the object type then cast it Ah yeah LOL Still seems like a very hacky workaround
Angius
Angius4w ago
And the listener would be, say,
Connection (object a) => {
if (a is IClientPacket icp)
{
// ...
}
}
Connection (object a) => {
if (a is IClientPacket icp)
{
// ...
}
}
Tofaa
Tofaa4w ago
I got it that's rather annoying but nothing i cando Thank you for your help :D
Angius
Angius4w ago
It's about the only thing I could think of, maybe someone else will have a better solution
Tofaa
Tofaa4w ago
I'll do it for now and see if i can find a better fix down the line
Angius
Angius4w ago
Maybe covariant/contravariant generics would help here, idk Making the T an in or out generic, etc
Tofaa
Tofaa4w ago
I'd need to look into how that works in c#
Angius
Angius4w ago
I'll be honest, I don't fully understand co/contravariance myself lol
Tofaa
Tofaa4w ago
I remember the concept from kotlin but its still mad hacky LOL