Consider the following generic class:
public class EntityChangeInfo<EntityType,TEntityKey>
{
ChangeTypeEnum ChangeType {get;}
TEntityKeyType EntityKey {get;}
}
Here EntityType
unambiguously defines TEntityKeyType
.
So it would be nice to have some kind of types’ map:
public class EntityChangeInfo<EntityType,TEntityKey> with map
< [ EntityType : Person -> TEntityKeyType : int]
[ EntityType : Car -> TEntityKeyType : CarIdType ]>
{
ChangeTypeEnum ChangeType {get;}
TEntityKeyType EntityKey {get;}
}
Another one example is:
public class Foo<TIn> with map
< [TIn : Person -> TOut1 : string, TOut2 : int, ..., TOutN : double ]
[TIn : Car -> TOut1 : int, TOut2 :int, ..., TOutN : Price ] >
{
TOut1 Prop1 {get;set;}
TOut2 Prop2 {get;set;}
...
TOutN PropN {get;set;}
}
The reasonable question: how can this be interpreted by the compiler?
Well, for me it is just the shortcut for two structurally similar classes:
public sealed class Foo<Person>
{
string Prop1 {get;set;}
int Prop2 {get;set;}
...
double PropN {get;set;}
}
public sealed class Foo<Car>
{
int Prop1 {get;set;}
int Prop2 {get;set;}
...
Price PropN {get;set;}
}
But besides this we could imaging some update of the Foo<>
:
public class Foo<TIn> with map
< [TIn : Person -> TOut1 : string, TOut2 : int, ..., TOutN : double ]
[TIn : Car -> TOut1 : int, TOut2 :int, ..., TOutN : Price ] >
{
TOut1 Prop1 {get;set;}
TOut2 Prop2 {get;set;}
...
TOutN PropN {get;set;}
public override string ToString()
{
return string.Format("prop1={0}, prop2={1},...propN={N-1},
Prop1, Prop2,...,PropN);
}
}
This all can seem quite superficial but the idea came when I was designing the messages for our system. The very first class. Many messages with the same structure should be discriminated by the EntityType
.
So the question is whether such construct exists in any programming language?
5
Yes, this facility does exist in some programming languages.
For example, in Haskell, these are called Functional Dependencies (fundeps are not part of Haskell 98, but they are supported in common implementations like GHC)
In C++, you can achieve this kind of thing with traits classes.
Well…you could just close the types for convenience:
public class EntityChangeInfo<TEntityType,TEntityKey>
{
ChangeTypeEnum ChangeType {get;}
TEntityKeyType EntityKey {get;}
}
public class PersonChangeInfo : EntityChangeInfo<Person,int>
{
}
public class CarChangeInfo : EntityChangeInfo<Car, CarIdType>
{
}
Though, it seems something like this would be more flexible:
public **interface** IEntityChangeInfo<TEntity,TEntityKey>
{
ChangeTypeEnum ChangeType {get;}
TEntityKey EntityKey {get;}
TEntity Entity {get;}
}
Also, this might be language dependent but I would suggest that even if you use an enum internally for convenience, you NOT expose the enum in the contract.
public **interface** IEntityChangeInfo<TEntity,TEntityKey>
{
int? ChangeTypeId {get;}
TEntityKey EntityKey {get;}
TEntity Entity {get;}
}
or
public **interface** IEntityChangeInfo<TEntity,TEntityKey,TChangeType>
{
TChangeType ChangeType {get;}
TEntityKey EntityKey {get;}
TEntity Entity {get;}
}
4
John Bartholomew’s answer is the correct exposition of the technique you’re referring to.
Leaving this incorrect answer here for a short while for posterity as it’s relevant related content. Will probably delete tomorrow when you’ve had time to review the information in case it’s valuable to you 🙂
There are a class of functional languages with a related concept, referred to as “dependent types” the idea being you can create functionality that depends on particular values of a type rather than just the type itself.
(that is my very limited description of the concept; crazy math-nuts people can speak to this more)
Here’s the wikipedia for some further reading, it lists a hand full of languages which implement dependent types.
One of the biggest uses of dependent types as I understand them is for proofs, which makes sense if you think about it and is similar to what you’re trying to use it for; you’re trying to constrain the types to get specific functionality in your type out of a type passed to you; dependent types works similarly but constraining down to the value level which is a very hardy constraint you could see being useful in the context of proving things are interacting in a strictly defined way.
Please; anyone more familiar with the concept of dependent types correct me if I’m off in my understanding. It’s a very high level concept and not one I’ve direct experience with beyond my addiction to reading about various languages, I wouldn’t want to be giving incorrect information here.
6
I think you are using generics for something that they were not intended and designed for.
Generics are to allow caller to create specific type from generic pattern. The generic type should never know what type caller will put in. It can limit those types, but never know exact types. Think how would you use those types? You cannot pass this to anything else, because it would mean this something else too needs to be generic and then you get “generic creep” where generic types get propagated through structure. I was there and it is not good at all.
What you are trying to achieve is extremely similar to code generation. You can use T4 or something and generate all required classes from some kind of definition. You should really forget about generics and think in pure OOP. Find something all those messages have in common that the message system can use and implement concrete class for each message type. This will make the design much more flexible, because you are not limited to two fields and will be much cleaner.
6