Generic type checking - c#

Is there a way to enforce/limit the types that are passed to primitives? (bool, int, string, etc.)
Now, I know you can limit the generic type parameter to a type or interface implementation via the where clause. However, this doesn't fit the bill for primitives (AFAIK) because they do not all have a common ground (apart from object before someone says! :P).
So, my current thoughts are to just grit my teeth and do a big switch statement and throw an ArgumentException on failure.
EDIT 1:
Just to clarify:
The code definition should be like this:
public class MyClass<GenericType> ....
And instantiation:
MyClass<bool> = new MyClass<bool>(); // Legal
MyClass<string> = new MyClass<string>(); // Legal
MyClass<DataSet> = new MyClass<DataSet>(); // Illegal
MyClass<RobsFunkyHat> = new MyClass<RobsFunkyHat>(); // Illegal (but looks awesome!)
EDIT 2
#Jon Limjap - Good point, and something I was already considering. I'm sure there is a generic method that can be used to determine if the type is of a value or reference type.
This could be useful in instantly removing a lot of the objects I don't want to deal with (but then you need to worry about the structs that are used such as Size ). Interesting problem no? :)
Here it is:
where T: struct
Taken from MSDN.
I'm curious. Could this be done in .NET 3.x using extension methods? Create an interface, and implement the interface in the extension methods (which would probably be cleaner than a bit fat switch). Plus if you then need to later extend to any lightweight custom types, they can also implement the same interface, with no changes required to the base code.
What do you guys think?
The sad news is I am working in Framework 2!! :D
EDIT 3
This was so simple following on from Jon Limjaps Pointer.. So simple I almost want to cry, but it's great because the code works like a charm!
So here is what I did (you'll laugh!):
Code added to the generic class
bool TypeValid()
{
// Get the TypeCode from the Primitive Type
TypeCode code = Type.GetTypeCode(typeof(PrimitiveDataType));
// All of the TypeCode Enumeration refer Primitive Types
// with the exception of Object and Empty (Null).
// Since I am willing to allow Null Types (at this time)
// all we need to check for is Object!
switch (code)
{
case TypeCode.Object:
return false;
default:
return true;
}
}
Then a little utility method to check the type and throw an exception,
private void EnforcePrimitiveType()
{
if (!TypeValid())
throw new InvalidOperationException(
"Unable to Instantiate SimpleMetadata based on the Generic Type of '" + typeof(PrimitiveDataType).Name +
"' - this Class is Designed to Work with Primitive Data Types Only.");
}
All that then needs to be done is to call EnforcePrimitiveType() in the classes constructors. Job done! :-)
The only downside, it only throws an exception at runtime (obviously) rather than design time. But that's no big deal and could be picked up with utilities like FxCop (which we don't use at work).
Special thanks to Jon Limjap on this one!

public class Class1<GenericType> where GenericType : struct
{
}
This one seemed to do the job..

Primitives appear to be specified in the TypeCode enumeration:
Perhaps there is a way to find out if an object contains the TypeCode enum without having to cast it to an specific object or call GetType() or typeof()?
Update It was right under my nose. The code sample there shows this:
static void WriteObjectInfo(object testObject)
{
TypeCode typeCode = Type.GetTypeCode( testObject.GetType() );
switch( typeCode )
{
case TypeCode.Boolean:
Console.WriteLine("Boolean: {0}", testObject);
break;
case TypeCode.Double:
Console.WriteLine("Double: {0}", testObject);
break;
default:
Console.WriteLine("{0}: {1}", typeCode.ToString(), testObject);
break;
}
}
}
It's still an ugly switch. But it's a good place to start!

Pretty much what #Lars already said:
//Force T to be a value (primitive) type.
public class Class1<T> where T: struct
//Force T to be a reference type.
public class Class1<T> where T: class
//Force T to be a parameterless constructor.
public class Class1<T> where T: new()
All work in .NET 2, 3 and 3.5.

If you can tolerate using factory methods (instead of the constructors MyClass you asked for) you could always do something like this:
class MyClass<T>
{
private readonly T _value;
private MyClass(T value) { _value = value; }
public static MyClass<int> FromInt32(int value) { return new MyClass<int>(value); }
public static MyClass<string> FromString(string value) { return new MyClass<string>(value); }
// etc for all the primitive types, or whatever other fixed set of types you are concerned about
}
A problem here is that you would need to type MyClass<AnyTypeItDoesntMatter>.FromInt32, which is annoying. There isn't a very good way around this if you want to maintain the private-ness of the constructor, but here are a couple of workarounds:
Create an abstract class MyClass. Make MyClass<T> inherit from MyClass and nest it within MyClass. Move the static methods to MyClass. This will all the visibility work out, at the cost of having to access MyClass<T> as MyClass.MyClass<T>.
Use MyClass<T> as given. Make a static class MyClass which calls the static methods in MyClass<T> using MyClass<AnyTypeItDoesntMatter> (probably using the appropriate type each time, just for giggles).
(Easier, but certainly weird) Make an abstract type MyClass which inherits from MyClass<AnyTypeItDoesntMatter>. (For concreteness, let's say MyClass<int>.) Because you can call static methods defined in a base class through the name of a derived class, you can now use MyClass.FromString.
This gives you static checking at the expense of more writing.
If you are happy with dynamic checking, I would use some variation on the TypeCode solution above.

You can simplify the EnforcePrimitiveType method by using typeof(PrimitiveDataType).IsPrimitive property. Am I missing something?

#Rob, Enum's will slip through the TypeValid function as it's TypeCode is Integer. I've updated the function to also check for Enum.
Private Function TypeValid() As Boolean
Dim g As Type = GetType(T)
Dim code As TypeCode = Type.GetTypeCode(g)
' All of the TypeCode Enumeration refer Primitive Types
' with the exception of Object and Empty (Nothing).
' Note: must also catch Enum as its type is Integer.
Select Case code
Case TypeCode.Object
Return False
Case Else
' Enum's TypeCode is Integer, so check BaseType
If g.BaseType Is GetType(System.Enum) Then
Return False
Else
Return True
End If
End Select
End Function

Having a similar challenge, I was wondering how you guys felt about the IConvertible interface. It allows what the requester requires, and you can extend with your own implementations.
Example:
public class MyClass<TKey>
where TKey : IConvertible
{
// class intentionally abbreviated
}
I am thinking about this as a solution, all though many of the suggested was part of my selection also.
My concern is - however - is it misleading for potential developers using your class?
Cheers - and thanks.

Use a custom FxCop rule that flags undesirable usage of MyClass<>.

In dotnet 6, I encountered this error when using struct:
The type 'string' must be a non-nullable value type in order to use it as parameter 'T'
So I use IConvertible instead
var intClass = new PrimitivesOnly<int>();
var doubleClass = new PrimitivesOnly<double>();
var boolClass = new PrimitivesOnly<bool>();
var stringClass = new PrimitivesOnly<string>();
var myAwesomeClass = new PrimitivesOnly<MyAwesomeClass>(); // illegal
// The line below encounter issue when using "string" type
// class PrimitivesOnly<T> where T : struct
class PrimitivesOnly<T> where T : IConvertible
{
}
class MyAwesomeClass
{
}

Related

Casting of generic type does not compile

Please have a look at the following code. Why do I get a compile-error?
I don't get it!
Casting is a way of telling the compiler that I know more about the objects than it does. And in this case, I know for fact, that "x" does actually contain an instance of "SomeClass". But the compiler seems to be unwilling to accept that information.
https://dotnetfiddle.net/0DlmXf
public class StrangeConversion
{
public class SomeClass { }
public interface ISomeInterface { }
public class Implementation : SomeClass, ISomeInterface { }
public void Foo<T>() where T : class
{
T x = (T)Factory();
//Compile-error: Cannot convert type 'T' to 'SomeClass'
SomeClass a = (SomeClass)x;
//This is perfectly fine:
SomeClass b = (SomeClass)(object)x;
if (x is SomeClass c)
{
//This works as well and 'c' contains the reference.
}
}
private object Factory()
{
return new Implementation();
}
}
Edit:
#Charles Mager has the correct answer in the comment: There does not seem to be a valid reason. The language designers just didn't want to allow this cast.
I fixed using the as casting e.g.
SomeClass a = x as SomeClass;
This Answer explains is very well https://stackoverflow.com/a/132467/16690008
Essentially it's because it would throw an exception if T is not type of that class
It's hard to make sense of exactly what you're trying to achieve, but it seems like a generic constraint is what you're after
public void Foo<T>()
where T : SomeClass // constrain T to be inheriting from SomeClass
{
T x = Factory<T>(); // x is guaranted to be SomeClass
}
private T Factory<T>()
where T : SomeClass // same here
{
return new Implementation();
}
You constrain the generic to only reference types by specifying where T : class, but the compiler needs to know with certainty if the cast is possible. This means you are asking the compiler to trust that SomeClass can be cast from any reference type you pass to it, which is something it won't do. The microsoft docs state that for the class generic type constraint:
The type argument must be a reference type. This constraint applies also to any class, interface, delegate, or array type.
Its important to note that SomeClass b = (SomeClass)(object)x; works because of the cast to object which is the root of the object hierarchy. But as you can see from the list of supported reference types, SomeClass a = (SomeClass)x; has to account for things such as delegates, array types, etc., at which point the compiler will throw you the error
Don't do SomeClass b = (SomeClass)(object)x;, it is much cleaner to make proper use of type constraints along with the as & is operators which were designed for this exact purpose of type checking and safe casting
Short answer:
This behaviour is correct according to the spec. The spec is just bad here since this might convert a compile-error into a runtime-error.
Long answer:
I did some more research on the matter. This is an oversight in the language's spec.
C# uses the same syntax for two totally different things:
int i = (int)1.9
This converts the double 1.9 to an integer. The value is actually changed.
object o = "abc";
string s = (string) o;
This looks the same, but does not change the object referenced by "o" at all. It does only convert the type of the reference.
When it comes to generics, this kind of ambiguity is an issue:
function f(T x) {
var x = (string) x;
}
What should the language do if T is "int"?
That's why the spec forces the developer to cast to object first:
function f(T x) {
var x = (string)(object)x;
}
Now, the behaviour is clear: X might still be a value-type. But if it is, it will be converted to a reference-type first.
This ambiguity does not exist in my example, since T is guaranteed to be a reference type:
public void Foo<T>() where T : class
Thus the cast to object is not necessary. It could even be harmful if the "where" specifies an actual type. In that case, the forced cast to object might convert a compile-time-error (impossible cast) to a runtime-error.
Unfortunately, the people who created the spec, did not see this issue and did not include it.

Switching on type with a generic return type

I'm working on making EF easier to unit test by writing some helpers that will make properties for me. I have a couple of backing fields
private Mock<DbSet<Workflow>> mockedWorkFlows;
private Mock<DbSet<WorkflowError>> mockedWorkFlowErrors;
And I want a generic function to be able to return me the correct backing field with the following function
public Mock<DbSet<T>> Mocked<T>(T t) where T : class
{
if ( (object)t is Workflow)
{
return mockedWorkFlows; //cannot Workflow to T
}
}
There are several private backing fields which I want to be returned based on the type passed it.
However, even if I add a class constraint of Workflow, I get the same error.
I also tried switching on t's type but no luck there either. The types of the several backing fields do not share a common ancestor, other than object. Is what I'm trying to do possible?
It is possible to seriously abuse C#7's switch to achieve what you want by switching on an unrelated value and using the var pattern with when guards:
public Mock<DbSet<T>> Mocked<T>() where T : class
{
switch(true)
{
case var _ when typeof(T) == typeof(Workflow):
return ...
case var _ when typeof(T) == typeof(WorkflowError):
return ...
default:
return null;
}
}
Being able to match on types in switch statements is a very common request. There are proposals for improvements to C# on the official language repo on github (see Proposal: switch on System.Type and pProposal: Pattern match via generic constraint). As and when more pattern matching functionality is added to C# (currently, set for "a 7.X release"), we may get nicer syntax for this functionality.
If I understand your intention correctly - you can do it like this:
// no need to pass instance of T - why?
public Mock<DbSet<T>> Mocked<T>() where T : class
{
if (typeof(T) == typeof(Workflow)) {
// first cast to object, then to return type to avoid compile error
// compiler does not know mockedWorkFlows is Mock<DbSet<T>>, but you
// know it already, because you checked type 'T'
return (Mock<DbSet<T>>) (object) mockedWorkFlows; //cannot Workflow to T
}
// etc
return null;
}
Whether it is good idea or not is a different story.

How to use generic declared variable in C#

public static object GetObject(int x)
{
return new object { };
}
public static object GetObject(string x)
{
return new object { };
}
public static void ProcessObject<T>(T x) where T : int, string <= got error here:
{
object o = GetObject(x);
}
Got error "A type used as a constraint must be an interface, a non-sealed class or a type parameter."
How can I rewrite the code to get it work without write ProcessObject(int x) and ProcessObject(string x) twice?
So what you have now (according to accepted answer and your comments) is:
public static void ProcessObject<T>(T x)
{
object o;
if (typeof(T) == typeof(int))
o = GetObject((int)(object)x);
else if (typeof(T) == typeof(string))
o = GetObject((string)(object)x);
else
throw new Exception();
// do stuff with o
}
I'd recommend making public int and string overloads, but to prevent code duplication, internally call another method:
public static void ProcessObject(int x)
{
ProcessObject(GetObject(x));
}
public static void ProcessObject(string x)
{
ProcessObject(GetObject(x));
}
private static void ProcessObject(object o)
{
// do stuff with o
}
This makes your public methods' input values clear: int and string are the only acceptable types, while still not duplicating your actual logic (// do stuff with o).
You might dislike that the two public ProcessObject methods are duplicates of each other, (on the surface anyway; under the hood, they're calling two different GetObject overloads) but I think it's the best option.
You cannot do what you are trying to do: first, it is not possible to list several classes in a generic constraint; second, the type that you can put in a constraint must be such that you could inherit it (or implement it if it is an interface). Both int and string fail this check. In cases like this, you would be better off with two separate overloads.
Just remove the where part
public static void ProcessObject<T>(T x)
{
object o = GetObject(x);
}
And also don't use object in your other methods, instead use T
it's impossible in C# take a look on Constraints on Type Parameters. Try to use dynamic
Generally speaking, if your object reacts differently based on the generic type argument, you probably shouldn't be using generics in this case. Generics are great for situations where you want to do always the same thing, no matter what the actual type used.
Therefore, generic constraints will only allow you to list one base class for a type argument. Any actual types passed to the respective type arguments are meant to be a part of the given inheritance hierarchy starting with the base class you specified, so users of your class can specify any type that matches the base class or any of its subclasses.
At the same time, you, the author of the generic class, can safely assume that the specified type has (at least) the interface of the base class indicated by the constraint. Hence, you may access any members of the base class.
If you want to allow either string or int, imagine what members that could be. Both are derived directly from System.Object, hence the restriction would make no sense as it is no restriction; every type is derived from System.Object.
Summarizing, if you really want to treat string and int differently, this is definitely a case for offering two overloads rather than one generic class.

Generic Class Members in C#?

Hey, I think I have the wrong idea here, but I'm not sure what is best. I want a class with a member variable that can be of any type, depending on what is needed at the time. So far, I have something like this:
public class ConfigSetting<T> {
private T value;
public T GetValue() {
return value;
}
public void ChangeValue() {
}
public ConfigSetting(string heading, string key) {
this.value = DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
}
}
The type returned by the right side of the 'this.value' line is a string, currently. I know here it seems like I have no need to use anything other than the string type, but eventually I will expand the constructor, such that 'this.value' could be a string, int, float, or bool.
Anyway, my compiler says "Cannot convert 'string' to 'T'", so I assume I'm doing something very backwards.
Thank you.
You're running into problems because this is not a good use of generics. If the generic type parameter can only be constructed in four different ways -- string, float, bool and int -- then this isn't very generic. I expect that a generic thing can be any type at all.
If I had a thing that could only be one of four types then I would model it like this:
abstract class ConfigSetting
{ /* shared code here */ }
class TextSetting : ConfigSetting
{ /* Code here to handle string settings */ }
class BooleanSetting : ConfigSetting
{ /* ...
and so on. I would probably then give each of them an internal constructor, and make the base class into a factory for the derived classes, using the factory pattern.
Only use generics if your solution is truly generic. Like List<T>, for example, can be a list of anything: ints, strings, arrays, dictionaries, functions, whatever. If the thing you are modeling has a small number of possible types, just make one for each type.
Well, what conversion did you expect it to apply? If you expect the value to already be of the right type, you could do:
object tmp = DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
this.value = (T) tmp;
Note that you have to go through object either implicitly (as here) or with an explicit cast:
this.value = (T)(object) DerivedMethods.configsettings... (etc);
The set of conversions provided for generic types is somewhat limited. But it should work if the original value is genuinely correct.
You need to cast the returned String to T:
public ConfigSetting(string heading, string key) {
this.value = (T)DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
}
I think you shouldn't necessarily use generics to plan ahead for all possible future scenarios because you will likely run into edge cases in the future and have to modify the code regardless.
However if you already have multiple scenarios that a generic would fit to, then you can benefit from the logic reuse now and can test them all properly.
I see others have already provided code answers so I'll stop here.
I assume that
DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue
is a string.
You can't assign a string to this.value because the type of this.value is T and could be anything, such as types to which strings are not assignable.
One solution is to remove the generic parameter and make value a string, and then provide different ways of accessing it which parse bools, floats, or whatever as required.
public float GetFloatValue {
get { return float.Parse(this.value); }
}
// etc
It looks like you're trying to assign a string (configsettings.SettingGroups[heading].Settings[key].RawValue) to the generic member. You'll need to provide some way to convert the string to type T -- usually through casting. For example:
this.value = (T)DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
In this line:
this.value = DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
you are setting your variable of type T to a string value. The RawValue method returns a string. You need to explicitly cast it to type T.
this.value = (T) (object) DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
Is T really only going to be only values without constructors? You might be able to make that explicit and avoid the object casting, if you want to avoid that for some reason.
Try a
string strValue = DerivedMethods.configsettings.SettingGroups[heading].Settings[key].RawValue;
this.value = (T)Convert.ChangeType(strValue, typeof(T), CultureInfo.InvariantCulture)
if you have your configuration values stored as strings and want it converted to the right type. This only works for most of the primitive types like Int32, Double, etc.

Operator as and generic classes

I want to make a method:
object Execute()
{
return type.InvokeMember(..);
}
to accept a generic parameter:
T Execute<T>()
{
return Execute() as T;
/* doesn't work:
The type parameter 'T' cannot be used with the 'as' operator because
it does not have a class type constraint nor a 'class' constraint */
// also neither typeof(T), nor T.GetType() are possible
return (T) Execute(); // ok
}
But I think operator as will be very useful: if result type isn't T method will return null, instead of an exception! Is it possible to do?
You need to add
where T : class
to your method declaration, e.g.
T Execute<T>() where T : class
{
By the way, as a suggestion, that generic wrapper doesn't really add much value. The caller can write:
MyClass c = whatever.Execute() as MyClass;
Or if they want to throw on fail:
MyClass c = (MyClass)whatever.Execute();
The generic wrapper method looks like this:
MyClass c = whatever.Execute<MyClass>();
All three versions have to specify exactly the same three entities, just in different orders, so none are any simpler or any more convenient, and yet the generic version hides what is happening, whereas the "raw" versions each make it clear whether there will be a throw or a null.
(This may be irrelevant to you if your example is simplified from your actual code).
You cannot use the as operator with a generic type with no restriction. Since the as operator uses null to represent that it was not of the type, you cannot use it on value types. If you want to use obj as T, T will have to be a reference type.
T Execute<T>() where T : class
{
return Execute() as T;
}
This small piece of code is an exception safe substitution for the as-keyword:
return Execute() is T value ? value : default(T)
It uses the pattern matching feature introduced with C# 7.
Use it, if you don't want to restrict the generic parameter to a reference type
It seems like you are just adding a wrapper method for casting to the type the user wants, thus only adding overhead to the execution. For the user, writing
int result = Execute<int>();
isn't much different from
int result = (int)Execute();
You can use the out modifier to write the result into a variable in the caller's scope, and return a boolean flag to tell whether it succeeded:
bool Execute<T>(out T result) where T : class
{
result = Execute() as T;
return result != null;
}
Is there a chance that Execute() might return a value type? If so, then you need Earwicker's method for class types, and another generic method for value types. Might look like this:
Nullable<T> ExecuteForValueType<T> where T : struct
The logic inside that method would say
object rawResult = Execute();
Then, you'd have to get the type of rawResult and see if it can be assigned to T:
Nullable<T> finalReturnValue = null;
Type theType = rawResult.GetType();
Type tType = typeof(T);
if(tType.IsAssignableFrom(theType))
{
finalReturnValue = tType;
}
return finalReturnValue;
Finally, make your original Execute message figure out which T is has (class or struct type), and call the appropriate implementation.
Note: This is from rough memory. I did this about a year ago and probably don't remember every detail. Still, I hope pointing you in the general direction helps.

Categories