How do I make this expression dynamic based on the generic type passed in the parameter?
In the simplified form:
public static class CompareService
{
public static List<T> Run<T>(List<T> database_list, string directory_path)
{
var csv_list = CompareService.MergeRecordsFromFiles<T>(directory);
return CompareService.RunComparison<T>(database_list, csv_list);
}
public static T CompareData<T>(List<T> database_list, List<T> csv_list)
{
var diff = new List<T>();
foreach (var db_item in database_list)
{
// ...
// if T is of type Deathstar compare reference_number property
// if T is of type Stormtrooper compare id property
// if T is of type Sith compare id and anger_level property
var csv_item = csv_list.FirstOrDefault(x => x.reference_number == db_item.reference_number);
// Comparison code
ComparisonResult result = compareLogic.Compare(db_item, csv_item);
// ...
}
return diff;
}
}
It is called from another generic service:
public static void Whatever<T>(List<T> list)
{
// ...
var directory_path = "C:\";
var delta = CompareService.CompareData<T>(list, directory_path);
// ...
}
The most naive implementation would be to check if your itemToFind can be cast to DeathStar, StormTrooper or Sith and if so call the instances property.
var deathStar = itemToFind as DeathStar;
if(deathStar != null)
return database_list.Where(x => ((DeathStar)x).reference_number == deathStar.reference_number).FirstOrDefault();
else
{
var sith = itemToFind as Sith;
if(sith != null)
return database_list.Where(x => ((Sith)x).anger_level == sith.anger_level).FirstOrDefault();
else
return database_list.Where(x => ((StormTrooper)x).id== ((StormTrooper)item).id).FirstOrDefault();
}
This is quite cumbersome, including many casts. In particular it completely bypasses the actual benefits of generics using any arbitrary type (that fullfills the constraints if existing). In your case you´d have a generic method that will only wortk for three decent types.
A better approach is to let all your classes implement a common interface that defines a property, for instance:
interface IObject {
int Level { get; }
}
Now all classes define that level-property:
clas DeathStar : IObject
{
public int Level { get { return this.reference_number; } }
}
clas Sith : IObject
{
public int Level { get { return this.anger_level; } }
}
clas StormTrooper: IObject
{
public int Level { get { return this.id; } }
}
Than you can use a constraint on your type T to implement that interface:
public static T CompareData<T>(List<T> list, T itemToFind) where T: IObject
Why not like this:
public static T CompareData<T>(List<T> list, Func<T, bool> predicate)
{
return database_list.FirstOrDefault(predicate);
}
And then use it like this:
var itemToFind = new ItemToFind();
var myObjectList = new List<MyObject>();
var item = CompareData<MyObject>(myObjectList, x=> x.MyObjectProperty == itemToFind.Id);
You could add a property selector:
public static class CompareService
{
public static T CompareData<T>(this List<T> list, T itemToFind, Func<T, int> propSelector)
{
int propToFind = propSelector(itemToFind); // cache
return database_list.FirstOrDefault(x => propSelector(x) == propToFind);
}
}
And call it like that:
listOfDeathstars.CompareData(deathStarToFind, ds => ds.reference_number);
listOfStormtroopers.CompareData(trooperToFind, t => t.id);
listOfSiths.CompareData(sithStarToFind, sith => new { sith.id, sith.anger_level});
Note: I added the this keyword in the signature to make it an extension (not sure if you intended that but forgot the keyword). And Where(predicate).FirstOrDefault() can be reduced to FirstOrDefault(predicate).
Is there a shorthand way to do it that does not involve loops?
public enum Item { Wood, Stone, Handle, Flint, StoneTool, Pallet, Bench }
public struct ItemCount
{
public Item Item;
public int Count;
}
private List<ItemCount> _contents;
so something like:
if(_contents.Contains(ItemCount i where i.Item == Item.Wood))
{
//do stuff
}
You don't need reflection, you can just use Linq:
if (_contents.Any(i=>i.Item == Item.Wood))
{
//do stuff
}
If you need the object/s with that value, you can use Where:
var woodItems = _contents.Where(i=>i.Item == Item.Wood);
You could do this using Linq extension method Any.
if(_contents.Any(i=> i.Item == Item.Wood))
{
// logic
}
In case if you need a matching object, do this.
var firstMatch = _contents.FirstOrDefault(i=> i.Item == Item.Wood);
if(firstMatch != null)
{
// logic
// Access firstMatch
}
I want to do something like this:
public List<T> GetList<T>()
{
if (typeof(T) == typeof(Type1))
{
return new List<Type1>() { new Type1(), new Type1(), new Type1() };
}
if (typeof(T) == typeof(Type2))
{
return new List<Type2>() {new Type2(), new Type2()};
}
throw new Exception("Unknown T");
}
public void DoStuffWithGenericList<T>()
{
var list = GetList<T>();
// do stuff that does not depend on T
}
But that, of course, is not legal. I feel I am missing something basic here :)
In my case, I am getting lists of different types of objects from Entity Framework, but the rest of my logic does not depend on the actual type. It can just work on List or it could be generic.
All Ts that GetList() will be called with as type parameter will inherit from the same base class, if it makes a difference.
Why not use the 'new' operator to instantiate the types:
public List<T> GetList<T>() where T : new()
{
if (typeof(T) == typeof(Type1))
{
return new List<T>() { new T() };
}
// etc...
throw new Exception("Unknown T");
}
All you have to do is ensure your types can be instantiated by adding the new() constraint.
Code like this cannot work because it depends on runtime type checks (you have written them explicitly). But how can the compiler know at compile time that the result of your runtime checks will be something that is actually a List<T>?
In this specific example case, you could achieve the desired aim with
public List<T> GetList<T>() where T : new()
{
if (typeof(T) == typeof(Type1))
{
return new List<T>() { new T(), new T(), new T() };
}
if (typeof(T) == typeof(Type2))
{
return new List<T>() { new T(), new T() };
}
throw new Exception("Unknown T");
}
But of course this does not solve any practical problem. If you have any specific question other than "why does this not work" in mind, you should edit the question to present it.
Consider this: to use GetList at some point in your code you will need to write
var x = GetList<SomeType>();
The type parameter SomeType must be hardcoded at the call site, otherwise the program will not compile. But if it must be hardcoded, then the above is not at all different from
public List<SomeType> GetListOfSomeType()
{
return new List<SomeType>();
}
var x = GetListOfSomeType();
So what are you trying to accomplish exactly?
Of course this counter-example is somewhat superficial, and in reality the generic version of GetList would allow increased flexibility if you are willing to use reflection. But again, in your example that is not the case.
public List<T> GetList<T>()
{
if (typeof(T) == typeof(Type1))
{
return new List<Type1>() { new Type1(), new Type1(), new Type1() }.Cast<T>().ToList();
}
if (typeof(T) == typeof(Type2))
{
return new List<Type2>() {new Type2(), new Type2()}.Cast<T>().ToList();
}
throw new Exception("Unknown T");
}
Just cast the return value since you already checked to make sure the type is right:
return (List<T>)(object)new List<Type1>(...
Whenever I'm going to see a if(typeof(T) == typeof(SomeType) i will switch to a dictionary which looks more or less this way:
public static class ListCreator
{
private static readonly Dictionary<Type, Func<object>> _Creators;
static ListCreator()
{
_Creators = new Dictionary<Type, Func<object>>();
InitializeDefaultCreators();
}
public static List<T> Create<T>()
{
Func<object> creator;
if (!_Creators.TryGetValue(typeof(T), out creator))
{
throw new InvalidOperationException("No creator available for type " + typeof(T).FullName);
}
return (List<T>)creator();
}
public static void Register<T>(Func<List<T>> creator)
{
_Creators.Add(typeof(T), creator);
}
public static void Register(Type type, Func<object> creator)
{
_Creators.Add(type, creator);
}
public static bool Unregister<T>()
{
return _Creators.Remove(typeof(T));
}
public static bool Unregister(Type type)
{
return _Creators.Remove(type);
}
private static void InitializeDefaultCreators()
{
Register(MyDoubleListCreator);
Register(typeof(int), () => Enumerable.Range(1, 15).ToList());
}
private static List<double> MyDoubleListCreator()
{
return Enumerable.Range(1, 10).Select(Convert.ToDouble).Select(val => val + 0.3).ToList();
}
}
This can than be used in these ways:
internal class Program
{
private static void Main(string[] args)
{
ListCreator.Register(SelfMadeList);
var someIntegers = ListCreator.Create<int>();
foreach (var item in someIntegers)
{
Console.WriteLine("Some integer: " + item);
}
var someDoubles = ListCreator.Create<double>();
foreach (var item in someDoubles)
{
Console.WriteLine("Some doubles: " + item);
}
var someTimeSpans = ListCreator.Create<TimeSpan>();
foreach (var item in someTimeSpans)
{
Console.WriteLine("Some timespans: " + item);
}
Console.ReadKey();
}
private static List<TimeSpan> SelfMadeList()
{
return Enumerable.Range(1, 20)
.Select(Convert.ToDouble)
.Select(val => val + 0.5)
.Select(TimeSpan.FromHours)
.ToList();
}
}
If the types are not derived from a common class, you can return a List<Object> and then cast the individual elements when you use them.
I have some class which have some code
public IEnumerable<TypeOne> TypeOne
{
get
{
if (db != null)
{
var col = db.Select<TypeOne>();
if (col.Count > 0) return col;
}
return db2.TypeOne;
}
}
public IEnumerable<TypeTwo> TypeTwo
{
get
{
if (db != null)
{
var col = db.Select<TypeTwo>();
if (col.Count > 0) return col;
}
return db2.TypeTwo;
}
}
So as You can see there is a lot of duplicated Code and there are same property name and item type of enumerable.
I want to call some property of object like "obj.MyProp". And MyProp must be resolved at runtime with some generic or non-generic method. Is it possible?
Slightly incomplete answer but you'll get the general idea:
This is a scenario where you want generics.
public IEnumerable<t> TypeSomething
{
get
{
if (db != null)
{
t col = db.Select<t>();
if (col.Count > 0) return col;
}
return GetDB<t>();
}
}
You'd need to implement GetDB() to return the appropriate db for any given type, but that'd be a single switch (or you can use reflection to find it)
There are a couple of ways to do this. The best is probably a generic method:
public IEnumerable<T> dbSelect<T>() //may need type constraints here
{
return db != null
? db.Select<T>()
: null;
}
public IEnumerable<TypeOne> TypeOne
{
get { return dbSelect<TypeOne> ?? db2.TypeOne; }
}
public IEnumerable<TypeTwo> TypeTwo
{
get { return dbSelect<TypeTwo>() ?? db2.TypeTwo; }
}
If your db2 object has a generic Select<T>-type method like db does, it's even easier:
public IEnumerable<T> dbSelect<T>()
{
return db != null
? db.Select<T>()
: db2.Select<T>(); //or db2.GetEntities<T>() or db2.OfType<T> or whatever
}
//Later, in your main code...
var x = dbSelect<TypeOne>();
var y = dbSelect<TypeTwo>();
This will be type safe, considerably faster than reflection, and will work with Intellisense.
You can solve this by using generics:
public IEnumerable<TypeOne> TypeOne
{
get { return GetTable<TypeOne>(); }
}
public IEnumerable<TypeTwo> TypeTwo
{
get { return GetTable<TypeTwo>(); }
}
private IEnumerable<T> GetTable<T>()
{
if (db != null)
{
var col = db.Select<T>();
if (col.Count > 0) return col;
}
return db2.Select<T>();
}
Is there a way to get the following function declaration?
public bool Foo<T>() where T : interface;
ie. where T is an interface type (similar to where T : class, and struct).
Currently I've settled for:
public bool Foo<T>() where T : IBase;
Where IBase is defined as an empty interface that is inherited by all my custom interfaces... Not ideal, but it should work... Why can't you define that a generic type must be an interface?
For what it's worth, I want this because Foo is doing reflection where it needs an interface type... I could pass it in as a normal parameter and do the necessary checking in the function itself, but this seemed a lot more typesafe (and I suppose a little more performant, since all the checks are done at compiletime).
The closest you can do (except for your base-interface approach) is "where T : class", meaning reference-type. There is no syntax to mean "any interface".
This ("where T : class") is used, for example, in WCF to limit clients to service contracts (interfaces).
I know this is a bit late but for those that are interested you can use a runtime check.
typeof(T).IsInterface
No, actually, if you are thinking class and struct mean classes and structs, you're wrong. class means any reference type (e.g. includes interfaces too) and struct means any value type (e.g. struct, enum).
To follow up on Robert's answer, this is even later, but you can use a static helper class to make the runtime check once only per type:
public bool Foo<T>() where T : class
{
FooHelper<T>.Foo();
}
private static class FooHelper<TInterface> where TInterface : class
{
static FooHelper()
{
if (!typeof(TInterface).IsInterface)
throw // ... some exception
}
public static void Foo() { /*...*/ }
}
I also note that your "should work" solution does not, in fact, work. Consider:
public bool Foo<T>() where T : IBase;
public interface IBase { }
public interface IActual : IBase { string S { get; } }
public class Actual : IActual { public string S { get; set; } }
Now there's nothing stopping you from calling Foo thus:
Foo<Actual>();
The Actual class, after all, satisfies the IBase constraint.
For some time now I've been thinking about near-compile-time constraints, so this is a perfect opportunity to launch the concept.
The basic idea is that if you cannot do a check compile time, you should do it at the earliest possible point in time, which is basically the moment the application starts. If all checks are okay, the application will run; if a check fails, the application will fail instantly.
Behavior
The best possible outcome is that our program doesn't compile if the constraints are not met. Unfortunately that's not possible in the current C# implementation.
Next best thing is that the program crashes the moment it's started.
The last option is that the program will crash the moment the code is hit. This is the default behavior of .NET. For me, this is completely unacceptable.
Prerequirements
We need to have a constraint mechanism, so for the lack of anything better... let's use an attribute. The attribute will be present on top of a generic constraint to check if it matches our conditions. If it doesn't, we give an ugly error.
This enables us to do things like this in our code:
public class Clas<[IsInterface] T> where T : class
(I've kept the where T:class here, because I always prefer compile-time checks to run-time checks)
So, that only leaves us with 1 problem, which is checking if all the types that we use match the constraint. How hard can it be?
Let's break it up
Generic types are always either on a class (/struct/interface) or on a method.
Triggering a constraint requires you to do one of the following things:
Compile-time, when using a type in a type (inheritance, generic constraint, class member)
Compile-time, when using a type in a method body
Run-time, when using reflection to construct something based on the generic base class.
Run-time, when using reflection to construct something based on RTTI.
At this point, I would like to state that you should always avoid doing (4) in any program IMO. Regardless, these checks won't support it, since it would effectively mean solving the halting problem.
Case 1: using a type
Example:
public class TestClass : SomeClass<IMyInterface> { ... }
Example 2:
public class TestClass
{
SomeClass<IMyInterface> myMember; // or a property, method, etc.
}
Basically this involves scanning all types, inheritance, members, parameters, etc, etc, etc. If a type is a generic type and has a constraint, we check the constraint; if it's an array, we check the element type.
At this point I must add that this will break the fact that by default .NET loads types 'lazy'. By scanning all the types, we force the .NET runtime to load them all. For most programs this shouldn't be a problem; still, if you use static initializers in your code, you might encounter problems with this approach... That said, I wouldn't advice anyone to do this anyways (except for things like this :-), so it shouldn't give you a lot of problems.
Case 2: using a type in a method
Example:
void Test() {
new SomeClass<ISomeInterface>();
}
To check this we have only 1 option: decompile the class, check all member tokens that are used and if one of them is the generic type - check the arguments.
Case 3: Reflection, runtime generic construction
Example:
typeof(CtorTest<>).MakeGenericType(typeof(IMyInterface))
I suppose it's theoretically possible to check this with similar tricks as case (2), but the implementation of it is much harder (you need to check if MakeGenericType is called in some code path). I won't go into details here...
Case 4: Reflection, runtime RTTI
Example:
Type t = Type.GetType("CtorTest`1[IMyInterface]");
This is the worst case scenario and as I explained before generally a bad idea IMHO. Either way, there's no practical way to figure this out using checks.
Testing the lot
Creating a program that tests case (1) and (2) will result in something like this:
[AttributeUsage(AttributeTargets.GenericParameter)]
public class IsInterface : ConstraintAttribute
{
public override bool Check(Type genericType)
{
return genericType.IsInterface;
}
public override string ToString()
{
return "Generic type is not an interface";
}
}
public abstract class ConstraintAttribute : Attribute
{
public ConstraintAttribute() {}
public abstract bool Check(Type generic);
}
internal class BigEndianByteReader
{
public BigEndianByteReader(byte[] data)
{
this.data = data;
this.position = 0;
}
private byte[] data;
private int position;
public int Position
{
get { return position; }
}
public bool Eof
{
get { return position >= data.Length; }
}
public sbyte ReadSByte()
{
return (sbyte)data[position++];
}
public byte ReadByte()
{
return (byte)data[position++];
}
public int ReadInt16()
{
return ((data[position++] | (data[position++] << 8)));
}
public ushort ReadUInt16()
{
return (ushort)((data[position++] | (data[position++] << 8)));
}
public int ReadInt32()
{
return (((data[position++] | (data[position++] << 8)) | (data[position++] << 0x10)) | (data[position++] << 0x18));
}
public ulong ReadInt64()
{
return (ulong)(((data[position++] | (data[position++] << 8)) | (data[position++] << 0x10)) | (data[position++] << 0x18) |
(data[position++] << 0x20) | (data[position++] << 0x28) | (data[position++] << 0x30) | (data[position++] << 0x38));
}
public double ReadDouble()
{
var result = BitConverter.ToDouble(data, position);
position += 8;
return result;
}
public float ReadSingle()
{
var result = BitConverter.ToSingle(data, position);
position += 4;
return result;
}
}
internal class ILDecompiler
{
static ILDecompiler()
{
// Initialize our cheat tables
singleByteOpcodes = new OpCode[0x100];
multiByteOpcodes = new OpCode[0x100];
FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
for (int num1 = 0; num1 < infoArray1.Length; num1++)
{
FieldInfo info1 = infoArray1[num1];
if (info1.FieldType == typeof(OpCode))
{
OpCode code1 = (OpCode)info1.GetValue(null);
ushort num2 = (ushort)code1.Value;
if (num2 < 0x100)
{
singleByteOpcodes[(int)num2] = code1;
}
else
{
if ((num2 & 0xff00) != 0xfe00)
{
throw new Exception("Invalid opcode: " + num2.ToString());
}
multiByteOpcodes[num2 & 0xff] = code1;
}
}
}
}
private ILDecompiler() { }
private static OpCode[] singleByteOpcodes;
private static OpCode[] multiByteOpcodes;
public static IEnumerable<ILInstruction> Decompile(MethodBase mi, byte[] ildata)
{
Module module = mi.Module;
BigEndianByteReader reader = new BigEndianByteReader(ildata);
while (!reader.Eof)
{
OpCode code = OpCodes.Nop;
int offset = reader.Position;
ushort b = reader.ReadByte();
if (b != 0xfe)
{
code = singleByteOpcodes[b];
}
else
{
b = reader.ReadByte();
code = multiByteOpcodes[b];
b |= (ushort)(0xfe00);
}
object operand = null;
switch (code.OperandType)
{
case OperandType.InlineBrTarget:
operand = reader.ReadInt32() + reader.Position;
break;
case OperandType.InlineField:
if (mi is ConstructorInfo)
{
operand = module.ResolveField(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes);
}
else
{
operand = module.ResolveField(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
}
break;
case OperandType.InlineI:
operand = reader.ReadInt32();
break;
case OperandType.InlineI8:
operand = reader.ReadInt64();
break;
case OperandType.InlineMethod:
try
{
if (mi is ConstructorInfo)
{
operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes);
}
else
{
operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
}
}
catch
{
operand = null;
}
break;
case OperandType.InlineNone:
break;
case OperandType.InlineR:
operand = reader.ReadDouble();
break;
case OperandType.InlineSig:
operand = module.ResolveSignature(reader.ReadInt32());
break;
case OperandType.InlineString:
operand = module.ResolveString(reader.ReadInt32());
break;
case OperandType.InlineSwitch:
int count = reader.ReadInt32();
int[] targetOffsets = new int[count];
for (int i = 0; i < count; ++i)
{
targetOffsets[i] = reader.ReadInt32();
}
int pos = reader.Position;
for (int i = 0; i < count; ++i)
{
targetOffsets[i] += pos;
}
operand = targetOffsets;
break;
case OperandType.InlineTok:
case OperandType.InlineType:
try
{
if (mi is ConstructorInfo)
{
operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes);
}
else
{
operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
}
}
catch
{
operand = null;
}
break;
case OperandType.InlineVar:
operand = reader.ReadUInt16();
break;
case OperandType.ShortInlineBrTarget:
operand = reader.ReadSByte() + reader.Position;
break;
case OperandType.ShortInlineI:
operand = reader.ReadSByte();
break;
case OperandType.ShortInlineR:
operand = reader.ReadSingle();
break;
case OperandType.ShortInlineVar:
operand = reader.ReadByte();
break;
default:
throw new Exception("Unknown instruction operand; cannot continue. Operand type: " + code.OperandType);
}
yield return new ILInstruction(offset, code, operand);
}
}
}
public class ILInstruction
{
public ILInstruction(int offset, OpCode code, object operand)
{
this.Offset = offset;
this.Code = code;
this.Operand = operand;
}
public int Offset { get; private set; }
public OpCode Code { get; private set; }
public object Operand { get; private set; }
}
public class IncorrectConstraintException : Exception
{
public IncorrectConstraintException(string msg, params object[] arg) : base(string.Format(msg, arg)) { }
}
public class ConstraintFailedException : Exception
{
public ConstraintFailedException(string msg) : base(msg) { }
public ConstraintFailedException(string msg, params object[] arg) : base(string.Format(msg, arg)) { }
}
public class NCTChecks
{
public NCTChecks(Type startpoint)
: this(startpoint.Assembly)
{ }
public NCTChecks(params Assembly[] ass)
{
foreach (var assembly in ass)
{
assemblies.Add(assembly);
foreach (var type in assembly.GetTypes())
{
EnsureType(type);
}
}
while (typesToCheck.Count > 0)
{
var t = typesToCheck.Pop();
GatherTypesFrom(t);
PerformRuntimeCheck(t);
}
}
private HashSet<Assembly> assemblies = new HashSet<Assembly>();
private Stack<Type> typesToCheck = new Stack<Type>();
private HashSet<Type> typesKnown = new HashSet<Type>();
private void EnsureType(Type t)
{
// Don't check for assembly here; we can pass f.ex. System.Lazy<Our.T<MyClass>>
if (t != null && !t.IsGenericTypeDefinition && typesKnown.Add(t))
{
typesToCheck.Push(t);
if (t.IsGenericType)
{
foreach (var par in t.GetGenericArguments())
{
EnsureType(par);
}
}
if (t.IsArray)
{
EnsureType(t.GetElementType());
}
}
}
private void PerformRuntimeCheck(Type t)
{
if (t.IsGenericType && !t.IsGenericTypeDefinition)
{
// Only check the assemblies we explicitly asked for:
if (this.assemblies.Contains(t.Assembly))
{
// Gather the generics data:
var def = t.GetGenericTypeDefinition();
var par = def.GetGenericArguments();
var args = t.GetGenericArguments();
// Perform checks:
for (int i = 0; i < args.Length; ++i)
{
foreach (var check in par[i].GetCustomAttributes(typeof(ConstraintAttribute), true).Cast<ConstraintAttribute>())
{
if (!check.Check(args[i]))
{
string error = "Runtime type check failed for type " + t.ToString() + ": " + check.ToString();
Debugger.Break();
throw new ConstraintFailedException(error);
}
}
}
}
}
}
// Phase 1: all types that are referenced in some way
private void GatherTypesFrom(Type t)
{
EnsureType(t.BaseType);
foreach (var intf in t.GetInterfaces())
{
EnsureType(intf);
}
foreach (var nested in t.GetNestedTypes())
{
EnsureType(nested);
}
var all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
foreach (var field in t.GetFields(all))
{
EnsureType(field.FieldType);
}
foreach (var property in t.GetProperties(all))
{
EnsureType(property.PropertyType);
}
foreach (var evt in t.GetEvents(all))
{
EnsureType(evt.EventHandlerType);
}
foreach (var ctor in t.GetConstructors(all))
{
foreach (var par in ctor.GetParameters())
{
EnsureType(par.ParameterType);
}
// Phase 2: all types that are used in a body
GatherTypesFrom(ctor);
}
foreach (var method in t.GetMethods(all))
{
if (method.ReturnType != typeof(void))
{
EnsureType(method.ReturnType);
}
foreach (var par in method.GetParameters())
{
EnsureType(par.ParameterType);
}
// Phase 2: all types that are used in a body
GatherTypesFrom(method);
}
}
private void GatherTypesFrom(MethodBase method)
{
if (this.assemblies.Contains(method.DeclaringType.Assembly)) // only consider methods we've build ourselves
{
MethodBody methodBody = method.GetMethodBody();
if (methodBody != null)
{
// Handle local variables
foreach (var local in methodBody.LocalVariables)
{
EnsureType(local.LocalType);
}
// Handle method body
var il = methodBody.GetILAsByteArray();
if (il != null)
{
foreach (var oper in ILDecompiler.Decompile(method, il))
{
if (oper.Operand is MemberInfo)
{
foreach (var type in HandleMember((MemberInfo)oper.Operand))
{
EnsureType(type);
}
}
}
}
}
}
}
private static IEnumerable<Type> HandleMember(MemberInfo info)
{
// Event, Field, Method, Constructor or Property.
yield return info.DeclaringType;
if (info is EventInfo)
{
yield return ((EventInfo)info).EventHandlerType;
}
else if (info is FieldInfo)
{
yield return ((FieldInfo)info).FieldType;
}
else if (info is PropertyInfo)
{
yield return ((PropertyInfo)info).PropertyType;
}
else if (info is ConstructorInfo)
{
foreach (var par in ((ConstructorInfo)info).GetParameters())
{
yield return par.ParameterType;
}
}
else if (info is MethodInfo)
{
foreach (var par in ((MethodInfo)info).GetParameters())
{
yield return par.ParameterType;
}
}
else if (info is Type)
{
yield return (Type)info;
}
else
{
throw new NotSupportedException("Incorrect unsupported member type: " + info.GetType().Name);
}
}
}
Using the code
Well, that's the easy part :-)
// Create something illegal
public class Bar2 : IMyInterface
{
public void Execute()
{
throw new NotImplementedException();
}
}
// Our fancy check
public class Foo<[IsInterface] T>
{
}
class Program
{
static Program()
{
// Perform all runtime checks
new NCTChecks(typeof(Program));
}
static void Main(string[] args)
{
// Normal operation
Console.WriteLine("Foo");
Console.ReadLine();
}
}
You cannot do this in any released version of C#, nor in the upcoming C# 4.0. It's not a C# limitation, either - there's no "interface" constraint in the CLR itself.
If possible, I went with a solution like this. It only works if you want several specific interfaces (e.g. those you have source access to) to be passed as a generic parameter, not any.
I let my interfaces, which came into question, inherit an empty interface IInterface.
I constrained the generic T parameter to be of IInterface
In source, it looks like this:
Any interface you want to be passed as the generic parameter:
public interface IWhatever : IInterface
{
// IWhatever specific declarations
}
IInterface:
public interface IInterface
{
// Nothing in here, keep moving
}
The class on which you want to put the type constraint:
public class WorldPeaceGenerator<T> where T : IInterface
{
// Actual world peace generating code
}
What you have settled for is the best you can do:
public bool Foo<T>() where T : IBase;
I tried to do something similar and used a workaround solution: I thought about implicit and explicit operator on structure: The idea is to wrap the Type in a structure that can be converted into Type implicitly.
Here is such a structure:
public struct InterfaceType
{
private Type _type;
public InterfaceType(Type type)
{
CheckType(type);
_type = type;
}
public static explicit operator Type(InterfaceType value)
{
return value._type;
}
public static implicit operator InterfaceType(Type type)
{
return new InterfaceType(type);
}
private static void CheckType(Type type)
{
if (type == null) throw new NullReferenceException("The type cannot be null");
if (!type.IsInterface) throw new NotSupportedException(string.Format("The given type {0} is not an interface, thus is not supported", type.Name));
}
}
basic usage:
// OK
InterfaceType type1 = typeof(System.ComponentModel.INotifyPropertyChanged);
// Throws an exception
InterfaceType type2 = typeof(WeakReference);
You have to imagine your own mecanism around this, but an example could be a method taken a InterfaceType in parameter instead of a type
this.MyMethod(typeof(IMyType)) // works
this.MyMethod(typeof(MyType)) // throws exception
A method to override that should returns interface types:
public virtual IEnumerable<InterfaceType> GetInterfaces()
There are maybe things to do with generics also, but I didn't tried
Hope this can help or gives ideas :-)
Solution A:
This combination of constraints should guarantee that TInterface is an interface:
class example<TInterface, TStruct>
where TStruct : struct, TInterface
where TInterface : class
{ }
It requires a single struct TStruct as a Witness to proof that TInterface is a struct.
You can use single struct as a witness for all your non-generic types:
struct InterfaceWitness : IA, IB, IC
{
public int DoA() => throw new InvalidOperationException();
//...
}
Solution B:
If you don't want to make structs as witnesses you can create an interface
interface ISInterface<T>
where T : ISInterface<T>
{ }
and use a constraint:
class example<TInterface>
where TInterface : ISInterface<TInterface>
{ }
Implementation for interfaces:
interface IA :ISInterface<IA>{ }
This solves some of the problems, but requires trust that noone implements ISInterface<T> for non-interface types, but that is pretty hard to do accidentally.
Use an abstract class instead. So, you would have something like:
public bool Foo<T>() where T : CBase;