C# Passing a Generic Class As a Method Parameter - c#

I have a CreateMessage class that is meant to handle incoming messages from a TCPClient, get the user type and return how the message should be formatted to its calling method.
In the method GetUserType, I want to pass UserBaseType as a parameter, which is a generic abstract class that takes a Type of UserType. However, it gives me the error:
Using the generic type 'UserTypeBase' requires one type argument.
I'm still trying to wrap my head around using generics and constraints, so I don't know if I'm going about this the wrong way. I've done a bit of digging to try to find a solution myself, but haven't found anything that more or less tailors to what I'm trying to do.
internal class CreateMessage
{
internal static string user;
internal static string message;
internal CreateMessage(string data)
{
user = Lists.users[data.Substring(1, 3)];
message = data.Substring(5, data.Length - 5);
}
private UserType GetUserType(UserTypeBase type)
{
return type.CreateType();
}
internal string Message()
{
UserType Type = null;
if (user.Contains("[M]"))
Type = GetUserType(UserMod);
else if (user.Contains("[B]"))
Type = GetUserType(UserVIP);
else
Type = GetUserType(UserRegular);
return Type.Message();
}
}
UserBaseType.cs
internal abstract class UserTypeBase<T> where T: UserType
{
public abstract string User { get; }
public abstract string Message { get; }
public abstract T CreateType();
}

You're going to want to make the method that takes the parameter generic as well. You're going to also want to mirror the type constraints as they appear on the parametric type to avoid compilation errors, as well as be explicit in what is and is not acceptable for the method.
private T GetUserType<T>(UserTypeBase<T> type) where T : UserType
{
return type.CreateType();
}
You can then call it with the type provided explicitly or implicitly, depending on the situation.
var someType = new UserTypeDerived<UserType>();
var resultImplicit = GetUserType(someType);
var resultExplicit = GetUserType<UserType>(someType);
Since it's a parameter that is being used generically, the compiler can implicitly determine what the expected value of T is based on the type of the parameter supplied.

Related

How to pass a generic type parameter reference to a function in C#?

I am trying to create a generic function which accepts any type of object and return it after the inquiry. However, the method I am creating is calling another generic function, which also accepts any type of object, but in my case I want to reference the class/type of "obj" to the GetInquiry() function in order for it to run, but according to this design, I won't be knowing its type at compile time, hence I am unable to call the GetInquiry() function.
public T Inquiry<T>(T obj)
{
string Username = "myUser";
Task<T> myTask = Inquire<T>.GetInquiry(Username); //throws 'T' must be a reference type
obj = myTask.Result;
return obj;
}
public static class Inquire<T> where T: class
{
public static async Task<T> GetInquiry(string Username)
{
//some code
}
}
How am I supposed to pass the reference? Any other workarounds and refactoring suggestions are also appreciated.
"the method I am creating is calling another generic function, which also accepts any type of object"
This statement is not correct...the method you're calling does NOT accept any type of object; it only accepts objects which are classes. In order for you to pass your type to it, you need to apply the same constraint:
public T Inquiry<T>(T obj) where T: class
{
string Username = "myUser";
Task<T> myTask = Inquire<T>.GetInquiry(Username); //throws 'T' must be a reference type
obj = myTask.Result;
return obj;
}
public static class Inquire<T>
{
public static Task<T> GetInquiry(string username)
{
//run process
Task<T> result = new Task<T>(() => {
//do something
});
return result;
}
}
The easiest way to solve this problem is to make the Inquiry method generic, like this:
public T Inquiry<T>(T obj)
where T : class
{
string Username = "myUser";
Task<T> myTask = Inquire<T>.GetInquiry(Username);
obj = myTask.Result;
return obj;
}
This way, you can specify the type parameter T to be a reference type, which will allow you to call the GetInquiry() method without any errors.

Assign more specific generic type to less specific type

I'd like to instantiate instances of a generic type and pass them around as if they were generic objects of an interface that they implement, such as below. Clearly, this is not allowed. Why is this, and what is the general practice for handling such situations? thanks.
public class MyType<T> where T : IComparable { }
MyType<IComparable> things = new MyType<Int32>();
this gets error:
Cannot implicitly convert type MyType<Int32> to MyType<IComparable>
I want to do this because I need different types of things that I want to pass around to more generic methods such as
public void DoSomething(MyType<IComparable> things) {...}
The assignment compatibility of generic type arguments does not make the generic type itself assignment compatible. This is why: Let's assume that we declared the generic class like this:
public class MyType<T> where T : IComparable
{
public T Value { get; set; }
}
And let's assume that this would compile ...
var intObject = new MyType<int> { Value = 42 };
MyType<IComparable> things = intObject; // Does not compile!
... then we could write
// things.Value has the static type IComparable
things.Value = "hello"; // Allowed because string is IComparable
But this is not possible since the underlying object is a MyType<int> and thus its Value property has the type int. Therefore, we are not allowed to substitute a MyType<int> for a MyType<IComparable>.

Matching constructor to counterpart in generic type definition

I have been thinking about this problem for a while and it feels like there must be a simple solution that I'm missing.
Let's say I have the following class:
public class Foo<T>
{
public Foo(T value)
{
}
public Foo(int value)
{
}
}
If I get all constructors on the type Foo<System.Int32> I will get back two constructors, both with a single parameter of type System.Int32 which cannot be differentiated.
If I get all constructors from the generic type definition of Foo<System.Int32> (Foo<T>) I will get back two constructors. One which accepts a generic parameter T and one that accepts a parameter of type System.Int32
// Will return two constructors with signatures that look identical.
var type = typeof(Foo<int>);
var ctors1 = type.GetConstructors();
// Will return two constructors as well. Parameters can be differentiated.
var genericTypeDefinition = typeof(Foo<int>).GetGenericTypeDefinition();
var ctors2 = genericTypeDefinition.GetConstructors();
Is there a way to match a constructor to its counterpart in its generic type definition?
For Comparing the ctors in both cases you can compare their MetadataToken.
Example:
foreach (var item in ctors1)
{
var ctorMatch = ctors2.SingleOrDefault(c => c.MetadataToken == item.MetadataToken);
}

Why do C# out generic type parameters violate covariance?

I'm unclear as to why the following code snippet isn't covarient?
public interface IResourceColl<out T> : IEnumerable<T> where T : IResource {
int Count { get; }
T this[int index] { get; }
bool TryGetValue( string SUID, out T obj ); // Error here?
}
Error 1 Invalid variance: The type parameter 'T' must be invariantly
valid on 'IResourceColl.TryGetValue(string, out T)'. 'T' is
covariant.
My interface only uses the template parameter in output positions. I could easily refactor this code to something like
public interface IResourceColl<out T> : IEnumerable<T> where T : class, IResource {
int Count { get; }
T this[int index] { get; }
T TryGetValue( string SUID ); // return null if not found
}
but I'm trying to understand if my original code actually violates covariance or if this is a compiler or .NET limitation of covariance.
The problem is indeed here:
bool TryGetValue( string SUID, out T obj ); // Error here?
You marked obj as out parameter, that still means though that you are passing in obj so it cannot be covariant, since you both pass in an instance of type T as well as return it.
Edit:
Eric Lippert says it better than anyone I refer to his answer to "ref and out parameters in C# and cannot be marked as variant" and quote him in regards to out parameters:
Should it be legal to make T marked as "out"? Unfortunately no. "out"
actually is not different than "ref" behind the scenes. The only
difference between "out" and "ref" is that the compiler forbids
reading from an out parameter before it is assigned by the callee, and
that the compiler requires assignment before the callee returns
normally. Someone who wrote an implementation of this interface in a
.NET language other than C# would be able to read from the item before
it was initialized, and therefore it could be used as an input. We
therefore forbid marking T as "out" in this case. That's regrettable,
but nothing we can do about it; we have to obey the type safety rules
of the CLR.
Here's the possible workaround using extension method. Not necessarily convenient from the implementor point of view, but user should be happy:
public interface IExample<out T>
{
T TryGetByName(string name, out bool success);
}
public static class HelperClass
{
public static bool TryGetByName<T>(this IExample<T> #this, string name, out T child)
{
bool success;
child = #this.TryGetByName(name, out success);
return success;
}
}
public interface IAnimal { };
public interface IFish : IAnimal { };
public class XavierTheFish : IFish { };
public class Aquarium : IExample<IFish>
{
public IFish TryGetByName(string name, out bool success)
{
if (name == "Xavier")
{
success = true;
return new XavierTheFish();
}
else
{
success = false;
return null;
}
}
}
public static class Test
{
public static void Main()
{
var aquarium = new Aquarium();
IAnimal child;
if (aquarium.TryGetByName("Xavier", out child))
{
Console.WriteLine(child);
}
}
}
It violates covariance because the value provided to output parameters must be of exactly the same type as the output parameter declaration. For instance, assuming T was a string, covariance would imply that it would be ok to do
var someIResourceColl = new someIResourceCollClass<String>();
Object k;
someIResourceColl.TryGetValue("Foo", out k); // This will break because k is an Object, not a String
Examine this little example and you will understand why it is not allowed:
public void Test()
{
string s = "Hello";
Foo(out s);
}
public void Foo(out string s) //s is passed with "Hello" even if not usable
{
s = "Bye";
}
out means that s must be definitely assigned before execution leaves the method and conversely you can not use s until it is definitely assigned in the method body. This seems to be compatible with covariance rules. But nothing stops you from assigning s at the call site before calling the method. This value is passed to the method which means that even if it is not usable you are effectively passing in a parameter of a defined type to the method which goes against the rules of covariance which state that the generic type can only be used as the return type of a method.

Generic Constraints vs. Inheritance

I'm trying to write some code to help unit test WCF services. These services are accessed through a facade class that creates the proxy instance, then calls the proxy method and returns the result; for each proxy method. I'd like to be able to replace the current creation code with something that either creates the real service or a fake one.
I couldn't get that to work. I boiled it down to the following:
using System.ServiceModel;
namespace ExpressionTrees
{
public interface IMyContract
{
void Method();
}
public class MyClient : ClientBase<IMyContract>, IMyContract
{
public MyClient()
{
}
public MyClient(string endpointConfigurationName)
: base(endpointConfigurationName)
{
}
public void Method()
{
Channel.Method();
}
}
public class Test
{
public TClient MakeClient<TClient>()
where TClient : ClientBase<IMyContract>, IMyContract, new()
{
return new MyClient("config");
// Error:
// Cannot convert expression of type 'ExpressionTrees.ServiceClient' to return type 'TClient'
}
}
}
Why is it that, even though the MyClient class derives from ClientBase<IMyContract> and implements IMyContract, that I can't return a MyClient instance from a method meant to return a TClient? TClient specifies a type constraint I had thought meant the same thing.
My goal was to call code like this:
public void CallClient<TClient>()
where TClient : ClientBase<IMyContract>, IMyContract
{
TClient client = null;
bool success = false;
try
{
client = MakeClient<TClient>();
client.Method();
client.Close();
success = true;
}
finally
{
if (!success && client != null)
{
client.Abort();
}
}
}
But instead of always calling MakeClient<TClient>, I wanted to be able to have a unit test inject a mock object. Since the code above depends both on ClientBase<IMyContract>, IMyContract, it seems I was trying to "synthesize" a generic class that would satisfy that constraint.
In retrospect, this wouldn't make sense. As an example, the ClientBase<IMyContract> would expect to be instantiated in such a way that a Channel object would be constructed that it could then delegate the Close method to.
I've wound up punting on having the exact same code run both for the real and for the fake services. I'm now injecting an IMyService, and either calling IMyService.Method or client.Method depending on whether my injected property is null.
Basically your code boils down to:
public static T MakeFruit<T>() where T : Fruit
{
return new Apple();
}
This always returns an apple, even if you call MakeFruit<Banana>(). But MakeFruit<Banana>() is required to return a banana, not an apple.
The meaning of the generic type constraint is that the type argument provided by the caller must match the constraint. So in my example, you can say MakeFruit<Banana>() but not MakeFruit<Tiger>() because Tiger does not match the constraint that T must be convertible to Fruit. I think you believe that the constraint means something else; I'm not sure what that is.
Think about it like this. A formal parameter has a formal parameter type. The formal parameter type restricts the type of the expression that is used as an argument. So when you say:
void M(Fruit x)
you are saying "the argument passed for formal parameter x in M must be convertible to Fruit".
Generic type parameter constraints are exactly the same; they are restrictions on what type arguments may be passed for the generic type parameters. When you say "where T : Fruit", that is just the same as saying (Fruit x) in the formal parameter list. T has got to be a type that goes to Fruit, just as the argument for x has got to be an argument that goes to Fruit.
Why do you even want to have a generic method in the first place? I don't understand what exactly you're trying to model with this method or why you want it to be generic.
You are restricting the TClient in the MakeClient<TClient>() portion of the call, not the return type.
The return type has to match the type of the generic parameter, but picture this:
public class MyOtherClient : ClientBase<IMyContract>, IMyContract
{
public void Method()
{
Channel.Method();
}
}
That's also a valid return by calling MakeClient<MyOtherClient>, which MyClient is not convertable to, since it should return a type of MyOtherClient.
Note that changing the return to:
return new MyClient() as TClient;
would probably get it past the compiler, but be null in my scenario above at runtime.
This should resolve your issue.
static T Make<T>() where T : IConvertible
{
var s = "";
return (T)(Object)s;
}

Categories

Resources