I am having trouble casting an object to a generic IList. I have a group of in statements to try to work around this, but there has to be a better way to do this.
This is my current method:
string values;
if (colFilter.Value is IList<int>)
{
values = BuildClause((IList<int>)colFilter.Value, prefix);
}
else if (colFilter.Value is IList<string>)
{
values = BuildClause((IList<string>)colFilter.Value, prefix);
}
else if (colFilter.Value is IList<DateTime>)
{
values = BuildClause((IList<DateTime>)colFilter.Value, prefix);
}
else if (...) //etc.
What I want to do is this:
values = BuildClause((IList<colFilter.ColumnType>)colFilter.Value, prefix);
or
values = BuildClause((IList<typeof(colFilter.ColumnType)>)colFilter.Value, prefix);
or
values = BuildClause((IList<colFilter.ColumnType.GetType()>)colFilter.Value, prefix);
Each of these produces this compiler error:
The type or namespace name 'colFilter' could not be found (are you missing a using directive or an assembly reference?)
In my example, colFilter.ColumnType is int, string, datetime, etc. I am not sure why this does not work.
Any ideas?
EDIT: This is C#2.0
EDIT #2
Here is the BuildClause method (I have overloads for each type):
private static string BuildClause(IList<int> inClause, string strPrefix)
{
return BuildClause(inClause, strPrefix, false);
}
private static string BuildClause(IList<String> inClause, string strPrefix)
{
return BuildClause(inClause, strPrefix, true);
}
private static string BuildClause(IList<DateTime> inClause, string strPrefix)
{
return BuildClause(inClause, strPrefix, true);
}
//.. etc for all types
private static string BuildClause<T>(IList<T> inClause, string strPrefix, bool addSingleQuotes)
{
StringBuilder sb = new StringBuilder();
//Check to make sure inclause has objects
if (inClause.Count > 0)
{
sb.Append(strPrefix);
sb.Append(" IN(");
for (int i = 0; i < inClause.Count; i++)
{
if (addSingleQuotes)
{
sb.AppendFormat("'{0}'", inClause[i].ToString().Replace("'", "''"));
}
else
{
sb.Append(inClause[i].ToString());
}
if (i != inClause.Count - 1)
{
sb.Append(",");
}
}
sb.Append(") ");
}
else
{
throw new Exception("Item count for In() Clause must be greater than 0.");
}
return sb.ToString();
}
There's no way to relate method overloading and generics: although they look similar, they are very different. Specifically, overloading lets you do different things based on the type of arguments used; while generics allows you to do the exact same thing regardless of the type used.
If your BuildClause method is overloaded and every overload is doing something different (not just different by the type used, but really different logic, in this case - choosing whether or not to add quotes) then somewhere, ultimately, you're gonna have to say something like "if type is this do this, if type is that do that" (I call that "switch-on-type").
Another approach is to avoid that "switch-on-type" logic and replace it with polymorphism. Suppose you had a StringColFilter : ColFilter<string> and a IntColFilter : ColFilter<int>, then each of them could override a virtual method from ColFilter<T> and provide its own BuildClause implementation (or just some piece of data that would help BuildClause process it). But then you'd need to explicitly create the correct subtype of ColFilter, which just moves the "switch-on-type" logic to another place in your application. If you're lucky, it'll move that logic to a place in your application where you have the knowledge of which type you're dealing with, and then you could explicitly create different ColFilters at different places in your application and process them generically later on.
Consider something like this:
abstract class ColFilter<T>
{
abstract bool AddSingleQuotes { get; }
List<T> Values { get; }
}
class IntColFilter<T>
{
override bool AddSingleQuotes { get { return false; } }
}
class StringColFilter<T>
{
override bool AddSingleQuotes { get { return true; } }
}
class SomeOtherClass
{
public static string BuildClause<T>(string prefix, ColFilter<T> filter)
{
return BuildClause(prefix, filter.Values, filter.AddSingleQuotes);
}
public static string BuildClause<T>(string prefix, IList<T> values, bool addSingleQuotes)
{
// use your existing implementation, since here we don't care about types anymore --
// all we do is call ToString() on them.
// in fact, we don't need this method to be generic at all!
}
}
Of course this also gets you to the problem of whether ColFilter should know about quotes or not, but that's a design issue and deserves another question :)
I also stand by the other posters in saying that if you're trying to build something that creates SQL statements by joining strings together, you should probably stop doing it and move over to parameterized queries which are easier and, more importantly, safer.
What does the function BuildClause() look like.
It seems to me that you can create BuildClause() as an extension method on IList, and you can append the values together. I assume that you just want to call .ToString() method on different types.
If you use generics properly in C# 3.0, you can achieve what you need through implicit typing (int C# 2.0 you might need to specify the type). If your BuildClause method is made generic, it should automatically take on whatever type is passed in to its generic parameter(s):
public IList<T> BuildClause<T>(IList<T> value, object prefix)
{
Type type = typeof(T);
if (type == typeof(string))
{
// handle string
}
else if (type == typeof(int))
{
// handle int
}
// ...
}
public class ColumnFilter<T>:
where T: struct
{
public IList<T> Value { get; set; }
}
var colFilter = new ColumnFilter<string>
{
Value = new { "string 1", "string 2", "string 3" }
}
IList<string> values = BuildClause(colFilter.Value, prefix);
With generics, you can drop the ColumnType property of your ColumnFilter. Since it is generic, along with your BuildClause method, you are easily able to determine the type by doing typeof(T).
I am having trouble casting an object to a generic
Casting is a run-time operation.
Generic is compile-time information.
Don't cross the streams.
Also - if you used a decent sql parameterization generator - it will add the single quotes for you.
I don't understand the question. It works for me. It could be as simple as droping the cast? What am I missing?
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1 {
class Foo<T> : List<T> {
}
class Program {
static void Main(string[] args) {
var a = new Foo<int>();
a.Add(1);
var b = new Foo<string>();
b.Add("foo");
Console.WriteLine(BuildClause(a, "foo", true));
Console.WriteLine(BuildClause(b, "foo", true));
}
private static string BuildClause<T>(IList<T> inClause, string strPrefix, bool addSingleQuotes) {
StringBuilder sb = new StringBuilder();
//Check to make sure inclause has objects
if (inClause.Count == 0)
throw new Exception("Item count for In() Clause must be greater than 0.");
sb.Append(strPrefix).Append(" IN(");
foreach (var Clause in inClause) {
if (addSingleQuotes)
sb.AppendFormat("'{0}'", Clause.ToString().Replace("'", "''"));
else
sb.Append(Clause.ToString());
sb.Append(',');
}
sb.Length--;
sb.Append(") ");
return sb.ToString();
}
}
}
The type for the IList must be known at compile time. Depending on what you want to do, you might be able to cast the list to an IList or IEnumerable (without generics) and then iterate over the objects
Related
Currently am doing a college project in C# which includes conversion of one form of code to another form of code, which involves choosing appropriate method/function from many methods available. The problem here is, to implement this using any pattern matching techniques rather then using many IF ELSE statements.
For now I have achieved this using nested IF ELSE statements which fills the whole program and looks like childish code on completion.
Current Implementation :--
Input:
//stored in list<string>
get(3 int) //type1
get(int:a,b,c) //type2
get(name) //type3
//list passed to ProcessGET method
Using if else :
public string ProcessGET(List<string> inputData)
{
foreach(var item in inputData)
{
if (inputData.item.Split('(')[1].Split(')')[0].Contains(':'))
{
return Type2 result;
}
else if (!inputData.item.Split('(')[1].Split(')')[0].Contains(':') && Convert.ToInt32(inputData.item.Split('(')[1].Split(')')[0].Split(' ')[0])>0)
{
return Type1 result;
}
else
{
return Type3 result;
}
}
}
How I wanted this to be is something like this,
/stored in list<string>
get(3 int) //type1
get(int:a,b,c) //type2
get(name) //type3
//list passed to ProcessGET method
public string ProcessGET(List<string> inputData)
{
foreach(var itm in inputData)
{
// call appropriate method(itm) based on type using some pattern matching techniques
}
}
string Method1(var data)
{
return result for type1;
}
string Method2(var data)
{
return result for type2;
}
string Method3(var data)
{
return result for type3;
}
Normally my program does mostly this kind of work for various types of input keywords like 'get','output','declare' etc etc etc... where Get is transformed to Scanf statements,output to printf statements and so on.
In such case if i use the IF ELSE, my project is full of If else statements.
As i just started to learn C#, I don't know if such thing exists(googled but didn't found what i was looking for), so any help regarding this problem will be very help(use)ful in further development.
Many Thanks in Advance.
Another general approach to this problem is to introduce an interface, say IMatcher. The interface has one method Match that returns either your type or maybe the fully transformed line.
You create multiple classes that implement IMatcher.
Your main loop then becomes:
var matchers = new [] { new MatcherA(), new MatcherB(), ... };
foreach (string line in input)
foreach (matcher in matchers)
{
var match = matcher.Match(line);
if (match != null) return match;
}
No more big if statement. Each matcher has its own small class and you can write unit tests for each. Also, use RegEx to make your matchers simpler.
I'll leave some suggestions here over which you can have a look at. Here's some basic code.
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace TestStuff
{
class Program
{
//Input string should be of the form "<type>:<index>"
static dynamic GiveMeSomethingDynamic(string someInput)
{
/* predefined arrays sothat we can return something */
string[] _storedStrings = { "Word 1", "word 2", "word 3" };
int[] _storedInts = { 1, 2, 3 };
float[] _storedFloats = { 3.14f, 2.71f, 42.123f };
/* Parse the input command (stringly typed functions are bad, I know.) */
string[] splitted = someInput.Split(':');
string wantedType = splitted[0];
int index = int.Parse(splitted[1]);
/* Decide what to return base on that argument */
switch (wantedType)
{
case "int":
return _storedInts[index];
case "string":
return _storedStrings[index];
case "float":
return _storedFloats[index];
//Nothing matched? return null
default:
return null;
}
}
static void Main(string[] args)
{
/* get some return values */
dynamic firstOutput = GiveMeSomethingDynamic("string:0");
dynamic secondOutput = GiveMeSomethingDynamic("int:1");
dynamic thirdOutput = GiveMeSomethingDynamic("float:2");
/* Display the returned objects and their type using reflection */
Console.WriteLine("Displaying returned objects.\n" +
"Object 1: {0}\t(Type: {1})\n" +
"Object 2: {2}\t\t(Type: {3})\n" +
"Object 3: {4}\t\t(Type: {5})\n",
firstOutput, firstOutput.GetType(),
secondOutput, secondOutput.GetType(),
thirdOutput, thirdOutput.GetType());
/* Act on the type of a object. This works for *all* C# objects, not just dynamic ones. */
if (firstOutput is string)
{
//This was a string! Give it to a method which needs a string
var firstOutputString = firstOutput as string; //Cast it. the "as" casting returns null if it couldn't be casted.
Console.WriteLine("Detected string output.");
Console.WriteLine(firstOutputString.Substring(0, 4));
}
//Another test with reflection.
Console.WriteLine();
//The list of objects we want to do something with
string[] values = { "string:abcdef", "int:12", "float:3.14" };
foreach(var value in values)
{
/* Parse the type */
string[] parsed = value.Split(':');
string _type = parsed[0];
string _argument = parsed[1];
switch (_type)
{
case "string":
//This is a string.
string _stringArgument = _argument as string;
Method1(_stringArgument);
break;
case "int":
//Do something with this int
int _intArgument = int.Parse(_argument);
Method2(_intArgument);
break;
case "float":
float _floatArgument = float.Parse(_argument);
Method3(_floatArgument);
break;
default:
Console.WriteLine("Unrecognized value type \"{0}\"!", _type);
break;
}
}
Console.ReadLine();
}
public static void Method1(string s) => Console.WriteLine("String Function called with argument \"{0}\"", s);
public static void Method2(int i) => Console.WriteLine("int Function called with argument {0}", i);
public static void Method3(float f) => Console.WriteLine("float Function called with argument {0}", f);
}
}
The first approach, given by the function GiveMeSomethingDynamic() relies on the dynamic keyword, which can return arbitrary types. Depending on the input string, it can return a string, an int or a float. The method is called in the Main() function and the type of the returned objects is checked with e.g. firstOutput is string (the is operator). It could have also been done withif( firstOutput.GetType() == typeof(string))`.
The second approach would be a classical "parse and cast" technique. We parse an input string of the format <type>:<value>, then call different functions with the converted or parsed arguments. This is maybe what you want.
There's also a "hacky" way giving a function arbitrary types. There, you can just the dynamic keyword on an input argument, as in
public dynamic superDynamic(dynamic inputVar)
{
//Figure out the type of that object
//return something dynamic
}
The "oldschool" approach (not using dynamic) would be to only pass object types into each function, but the parsing is equivalent (if(someArgument.GetType() == typeof(string))...).
Hope this gives you some ideas on how to parse these strings, cast them to different types and call different functions with it.
So the types are stored as strings in a list, right? And you want to call a different function based on the value of the string?
Here's how I would finish your code:
Create an interface:
public interface IMyType
{
string Result();
string Input {get; set;}
}
and three classes which implement it:
public class Type1 : IMyType
{
public string Result()
{
// do something
}
public string Input {get; set;}
}
(repeat for Type2 and Type3)
3.then create a method which returns one of these three types
based on pattern matching your string input
public IMyType GetAppropriateType(string input)
{
if (inputData.item.Split('(')[1].Split(')')[0].Contains(':'))
{
return new Type2 {Input = input};
}
//etc
}
public string ProcessGET(List<string> inputData)
{
foreach(var itm in inputData)
{
IMyType type = GetAppropriateType(itm);
type.Result();
}
}
Probably worth looking at regex for your string matching too
I'm currently refactoring a large codebase and converting it from vb.net to c#. One of the functions does some funky logic around loading columns from a datasource and putting them into properties.
With vb, the creator didn't need to worry about conversions etc. but with c# I need to consider it.
So, given a piece of code like the below, I need to convert an int to the relevant enum type.
public class SomeType
{
public SomeEnum enumValue { get; set; }
public void SetValues()
{
... GetValuesFromDataSource
... Iterates values
enumValue = SetProperty(enumValue, valueFromDataSource);
}
}
protected dynamic SetProperty(object oProperty, object oValue)
{
if (oProperty is Enum)
{
... Convert oValue to the relevant enum type and assign to oProperty
}
}
Just to add some clarity. The references to SetProperty number in the thousands, so I don't want to refactor by hand.
The oValue will come in as an int. There is other logic in the SetProperty method for other types. oProperty could be one of any of the hundreds of enums in the application.
If valueFromDataSource is an object and is just the boxed enum in question, you can use something like the following
if (valueFromDataSource is someEnum)
{
return (someEnum)valueFromDataSource
}
else if (valueFromDataSource is someOtherEnum)
{
return (someOtherEnum)valueFromDataSource
}
However in your original function you're returning back an object, which will box it again, nothing gained
Which leads me to believe your valueFromDataSource is actually an int, therefore, there is no way back, and you would at best have to guess what the enum should be
In a design scenario, your best bet is not store the enum as an int, and just box it
Second would be to leave a hint to the enum type or something
Update
can you convert an int to an Enum without knowing the type at design time?
Unfortunately the answer is no, in VB or C# or any other language, it just cant be done
If your re-factoring code, you must have some wires crossed as the VB creator couldn't do this either
Update to do it more dynamically, using an out parameter for a SetProperty Method
class Program
{
static void Main(string[] args)
{
Entity tt = default(Entity);
Test tester = default(Test);
tester = SetProperty(tester, 5);
tt = SetProperty(tt, "Test7");
Console.WriteLine(tester);
Console.WriteLine(tt);
Console.ReadLine();
}
public static T Cast<T>(T target, object Source)
{
return (T)Source;
}
public static dynamic SetProperty<T>(T target, object source)
{
Type t = typeof(T);
Type sourceType = source.GetType();
if (t.IsEnum)
{
Type enumType = t.GetEnumUnderlyingType();
if (Type.Equals(sourceType, enumType))
{
return (T)Cast(Activator.CreateInstance(enumType), source);
}
if (Type.Equals(sourceType, typeof(string)))
{
Array names = t.GetEnumValues();
foreach (var name in names)
{
if (name.ToString() == source.ToString())
{
return (T)name;
}
}
}
return default(T);
}
return null;
}
public enum Entity
{
Test5,
Test7,
Test8
}
public enum Test : int
{
Test1 = 1,
Test2 = 5,
Test4 = 7
}
}
the output would then be:
Test2
Test7
Say I define a variable like this:
var o = new { RBI = 108, Name = "Roberto Alomar" };
I can do something like:
Console.WriteLine("{0}", o);
But if I try:
foreach (var i in o) {
Console.WriteLine("{0}", o[i]);
}
I get an error:
foreach statement cannot operate on variables of type 'AnonymousType#1' because 'AnonymousType#1' does not contain a public definition for 'GetEnumerator'
So how does it work under the hood? I'd think that a method for turning an object into a string would have to loop through all the properties to accomplish the task. Is there some special method that allows this to happen, or am I misunderstanding how this works?
How does it work under the hood? I'd think that a method for turning an object into a string would have to loop through all the properties to accomplish the task.
Your assumption is that the implementation of ToString is shared between all instances of all anonymous types; that, for example, there is some helper that is logically something like you would do in JavaScript:
var s = "";
for (property in this)
s += property + ":" + this[property];
This assumption is wrong; there is no single one-size-fits-all implementation of ToString for anonymous types. Rather, the compiler knows what all the properties of the anonymous method are and so it generates a brand-new custom implementation of ToString for every distinct anonymous type.
In C#, the foreach loop does not do what the for-in loop does in JavaScript. The C# loop enumerates the members of a collection. The JS loop enumerates the properties of an object.
If you want to enumerate the properties of an object in C# you can do that, it just takes a bit more work:
var s = "";
foreach (PropertyInfo propertyInfo in this.GetType().GetProperties())
s += propertyInfo.Name + ":" + propertyInfo.GetValue(this).ToString();
You cannot do that because anonymous types don't implements IEnumerable interface - it's not a collection just one object. You have to explicitly print the values.
Console.WriteLine("{0}", o.RBI);
Console.WriteLine("{0}", o.Name);
But step yourself back. Do you need anonymous type? Define your own custom type.
class MyType // give it more meaningful name
{
public int RBI { get; set;}
public string Name { get; set;}
}
All anonymous objects have the same methods. They can be compared to each other as long as they have the same named fields with the same types, they all have a ToString() implementation that will give the string as you can see. But they don't have an implementation of an enumerator. Why should they? It's not like Javascript in that sense where you can enumerate over the property names/indices/whatever because... it's C#, that's just not how it is. Why would you think any different?
If you wanted something to work similarly, fortunately we have implicitly typed variables and reflection to help us out there.
var obj = new { Foo = "asd", Bar = "add", Gar = "123" };
var adapter = PropertyAdapter.Create(obj);
foreach (var name in adapter)
Console.WriteLine("obj.{0} = {1}", name, adapter[name]);
public static class PropertyAdapter
{
public static PropertyAdapter<T> Create<T>(T obj)
{
return new PropertyAdapter<T>(obj);
}
}
public class PropertyAdapter<T> : IEnumerable<string>
{
private T obj;
public PropertyAdapter(T obj) { this.obj = obj; }
public override string ToString()
{
return obj.ToString();
}
public object this[string name]
{
get
{
return typeof(T).GetProperty(name).GetValue(obj, null);
}
}
public IEnumerator<string> GetEnumerator()
{
return typeof(T)
.GetProperties()
.Select(pi => pi.Name)
.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
When you do
Console.WriteLine("{0}", o);
What's really happening is a call to Object.ToString(), which is inherited and has a built-in implementation for anonymous types that prints the properties and values.
On the other hand,
foreach (var i in o) { .. }
Can't work, because o must be an IEnumerable (or IEnumerable<>)
EDIT: The equivalent of what your expecting Enumerating over the string that is printed when using WriteLine can be achieved (for explanatory purposes, and otherwise useless) by doing:
foreach (var i in o.ToString()) { .. }
However as Jeff Mercado points out, that is not what you seem to want (it won't iterate over the properties - only over the individual characters of the already-formatted string)
This is a bit difficult to explain. So here it goes.
I have a function like this:
public T FooBar<T>(Func<T> function)
{
T returnData = function();
// want to iterate through returnData to do something to it
return returnData;
}
If the returnData (T) is an IEnumerable list, then I would like to enumerate through returnData to modify its contents using reflection. But I can't seem to be able to do it. When I try to cast returnData to an enumerable type, I get an exception:
Unable to cast object of type
'System.Collections.Generic.List`1[Cars]'
to type
'System.Collections.Generic.List`1[System.Object]'.
I will not know that the return type will be a list of 'cars' for example ahead of time, only at run time. So I have to check using reflection if it is a list, and then try to cast it so that I can enumerate through it.
Unless I am going about it the wrong way. How can I enumerate through returnData if it is of type T?
One approach is to add a type constraint on T, but this is not ideal:
public T FooBar<T>(Func<T> function) where T : IEnumerable
{
// T is not strongly typed for the enumerated item
If you changed your method slightly (w.r.t. T):
public IEnumerable<T> FooBar<T>(Func<IEnumerable<T>> function)
Then you have strong typing on the actual item being enumerated with the added bonus of accepting enumerable objects.
So I noticed from a second read of your question, there is some confusion about what T means for your variable returnData. In the case where FooBar() is passed a List<Car>, T is List<Car>, and really has no association with the generic type specification of the List<> itself. You can think of it as some List<U> where U is some other, unknown type.
At runtime you will have no simple way to get to U as it is hidden, so to speak, inside T. You could use overloading as some of the other answerers recommend, and provide a non-IEnumerable<U> method and one which takes arguments of type Func<IEnumerable<T>>.
Perhaps with some more details about the goal of FooBar<T> we could make some more specific recommendations.
if (returnData is System.Collections.IEnumerable)
{
foreach (object o in (System.Collections.IEnumerable)returnData)
{
// Do something.
}
}
Really, though, why not have an additional overload like this:
public T FooBar<T>(Func<IEnumerable<T>> function)
Have you tried type casting to IEnumerable instead of IEnumerable<T>? With IEnumerable you can still use it in a foreach loop. The variable each item would go in should be of type object i.e.:
foreach(object item in (IEnumerable)T){...}
You should check first to be sure that T implements IEnumerable.
The issue here is IEnumerable and IEnumerable Of T are not the same... but you can check for the difference and account for it in your code. Note that IEnumerable Of T inherits IEnumerable, so you can wrap the check for the generic version inside the non-generic version.
The following worked for me in a small test I wrote - I hope it is sufficient for you to do what you need.
Here is the meat and potatoes:
class FooBarOfT
{
public T FooBar<T>(Func<T> function)
{
T returnData = function();
//Want to iterate through returnData to do something to it.
if (returnData is IEnumerable)
{
// get generic type argument
var returnDataType = returnData.GetType();
if (returnDataType.IsGenericType)
{
// this is a System.Collections.Generic.IEnumerable<T> -- get the generic type argument to loop through it
Type genericArgument = returnDataType.GetGenericArguments()[0];
var genericEnumerator =
typeof(System.Collections.Generic.IEnumerable<>)
.MakeGenericType(genericArgument)
.GetMethod("GetEnumerator")
.Invoke(returnData, null);
IEnumerator enm = genericEnumerator as IEnumerator;
while (enm.MoveNext())
{
var item = enm.Current;
Console.WriteLine(string.Format("Type : {0}", item.GetType().Name));
}
}
else
{
// this is an System.Collections.IEnumerable (not generic)
foreach (var obj in (returnData as IEnumerable))
{
// do something with your object
}
}
}
return returnData;
}
}
I also set up some supporting test classes:
class Foo
{
private string _fooText;
public Foo(string fooText)
{
_fooText = fooText;
}
public string Execute()
{
return string.Format("executed! with {0} !", _fooText);
}
}
class Bar
{
public string BarContent { get; set; }
}
And a small console app to run some tests:
class Program
{
static void Main(string[] args)
{
// tests
Func<string> stringFunc = () =>
"hello!";
Func<List<Foo>> listFooFunc = () =>
new List<Foo>
{
new Foo("Hello!"),
new Foo("World!")
};
Func<IEnumerable> ienumerableFooFunc = () =>
new Hashtable
{
{ "ItemOne", "Foo" },
{ "ItemTwo", "Bar" }
};
var fooBarOfT = new FooBarOfT();
fooBarOfT.FooBar(stringFunc);
fooBarOfT.FooBar(listFooFunc);
fooBarOfT.FooBar(ienumerableFooFunc);
Console.ReadKey();
}
}
Consider the following (heavily simplified) code:
public T Function<T>() {
if (typeof(T) == typeof(string)) {
return (T) (object) "hello";
}
...
}
It's kind of absurd to first cast to object, then to T. But the compiler has no way of knowing that the previous test assured T is of type string.
What is the most elegant, idiomatic way of achieving this behavior in C# (which includes getting rid of the stupid typeof(T) == typeof(string), since T is string can't be used)?
Addendum: There is no return type variance in .net, so you can't make a function overload to type string (which, by the way, is just an example, but one reason why association end redefinition in polymorphism, e.g. UML, can't be done in c#). Obviously, the following would be great, but it doesn't work:
public T Function<T>() {
...
}
public string Function<string>() {
return "hello";
}
Concrete Example 1: Because there's been several attacks to the fact that a generic function that tests for specific types isn't generic, I'll try to provide a more complete example. Consider the Type-Square design pattern. Here follows a snippet:
public class Entity {
Dictionary<PropertyType, object> properties;
public T GetTypedProperty<T>(PropertyType p) {
var val = properties[p];
if (typeof(T) == typeof(string) {
(T) (object) p.ToString(this); // magic going here
}
return (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(val);
}
}
Concrete Example 2: Consider the Interpreter design pattern:
public class Expression {
public virtual object Execute() { }
}
public class StringExpression: Expression {
public override string Execute() { } // Error! Type variance not allowed...
}
Now let's use generics in Execute to allow the caller to force a return type:
public class Expression {
public virtual T Execute<T>() {
if(typeof(T) == typeof(string)) { // what happens when I want a string result from a non-string expression?
return (T) (object) do_some_magic_and_return_a_string();
} else if(typeof(T) == typeof(bool)) { // what about bools? any number != 0 should be True. Non-empty lists should be True. Not null should be True
return (T) (object) do_some_magic_and_return_a_bool();
}
}
}
public class StringExpression: Expressiong {
public override T Execute<T>() where T: string {
return (T) string_result;
}
}
If you're making these types of checks in a generic method, I'd rethink your design. The method is obviously not truly generic - if it were, you wouldn't need specific type checking...
Situations like this typically can be handled more cleanly by a redesign. One alternative is often to provide an overload of the appropriate type. Other design alternatives which avoid the type-specific behavior exist, as well, such as Richard Berg's suggestion of passing in a delegate.
using System;
using System.Collections.Generic;
using System.Linq;
namespace SimpleExamples
{
/// <summary>
/// Compiled but not run. Copypasta at your own risk!
/// </summary>
public class Tester
{
public static void Main(string[] args)
{
// Contrived example #1: pushing type-specific functionality up the call stack
var strResult = Example1.Calculate<string>("hello", s => "Could not calculate " + s);
var intResult = Example1.Calculate<int>(1234, i => -1);
// Contrived example #2: overriding default behavior with an alternative that's optimized for a certain type
var list1 = new List<int> { 1, 2, 3 };
var list2 = new int[] { 4, 5, 6 };
Example2<int>.DoSomething(list1, list2);
var list1H = new HashSet<int> { 1, 2, 3 };
Example2<int>.DoSomething<HashSet<int>>(list1H, list2, (l1, l2) => l1.UnionWith(l2));
}
}
public static class Example1
{
public static TParam Calculate<TParam>(TParam param, Func<TParam, TParam> errorMessage)
{
bool success;
var result = CalculateInternal<TParam>(param, out success);
if (success)
return result;
else
return errorMessage(param);
}
private static TParam CalculateInternal<TParam>(TParam param, out bool success)
{
throw new NotImplementedException();
}
}
public static class Example2<T>
{
public static void DoSomething(ICollection<T> list1, IEnumerable<T> list2)
{
Action<ICollection<T>, IEnumerable<T>> genericUnion = (l1, l2) =>
{
foreach (var item in l2)
{
l1.Add(item);
}
l1 = l1.Distinct().ToList();
};
DoSomething<ICollection<T>>(list1, list2, genericUnion);
}
public static void DoSomething<TList>(TList list1, IEnumerable<T> list2, Action<TList, IEnumerable<T>> specializedUnion)
where TList : ICollection<T>
{
/* stuff happens */
specializedUnion(list1, list2);
/* other stuff happens */
}
}
}
/// I confess I don't completely understand what your code was trying to do, here's my best shot
namespace TypeSquarePattern
{
public enum Property
{
A,
B,
C,
}
public class Entity
{
Dictionary<Property, object> properties;
Dictionary<Property, Type> propertyTypes;
public T GetTypedProperty<T>(Property p)
{
var val = properties[p];
var type = propertyTypes[p];
// invoke the cast operator [including user defined casts] between whatever val was stored as, and the appropriate type as
// determined by the domain model [represented here as a simple Dictionary; actual implementation is probably more complex]
val = Convert.ChangeType(val, type);
// now create a strongly-typed object that matches what the caller wanted
return (T)val;
}
}
}
/// Solving this one is a straightforward application of the deferred-execution patterns I demonstrated earlier
namespace InterpreterPattern
{
public class Expression<TResult>
{
protected TResult _value;
private Func<TResult, bool> _tester;
private TResult _fallback;
protected Expression(Func<TResult, bool> tester, TResult fallback)
{
_tester = tester;
_fallback = fallback;
}
public TResult Execute()
{
if (_tester(_value))
return _value;
else
return _fallback;
}
}
public class StringExpression : Expression<string>
{
public StringExpression()
: base(s => string.IsNullOrEmpty(s), "something else")
{ }
}
public class Tuple3Expression<T> : Expression<IList<T>>
{
public Tuple3Expression()
: base(t => t != null && t.Count == 3, new List<T> { default(T), default(T), default(T) })
{ }
}
}
Can you use as here?
T s = "hello" as T;
if(s != null)
return s;
I can't think of an "elegant" way to do this. As you say, the compiler can't know that the conditional has ensured that the type of T is string. As a result, it has to assume that, since there's no generalized way to convert from string to T, it's an error. object to T might succeed, so the compiler allows it.
I'm not sure I'd want an elegant way to express this. Although I can see where it'd be necessary to do explicit type checks like this in some situations, I think I'd want it to be cumbersome because it really is a bit of a hack. And I'd want it to stick out: "Hey! I'm doing something weird here!"
Ok, I took a run at it from several different angles and came up short. I would have to conclude that if your current implementation gets the job done you should take the win and move on. Short of some arcane emissions what you got is what you get.
But the compiler has no way of knowing
that the previous test assured T is of
type string.
Umm.... If I am not mistaken, generics is just code gen. The compiler generates a matching method for each distinct type found in the calling methods. So the compiler does know the type argument for the overload being called. Again; If I am not mistaken.
But overall, i think you are misusing the generic in this case, from what I can see, and as others have stated, there are more appropriate solutions..... which are unnamable unless you post code that completely specifies your requirements.
just my 2 pesos...