Given the following function:
public static T GetCachedBoxedValue<T>(string cacheKey, Func<T> getFromExternFunction, bool skipLocalCaches = false)
{
var r = Manager.Get(
cacheKey,
() => new Tuple<T>(
getFromExternFunction()
),
skipLocalCaches
);
if (r == null || r.Item1 == null)
{
return default;
}
return r.Item1;
}
When T is a List<Guid> and the r value is null, it returns null. How would I modify this to return a new empty list?
You can add the new constraint to the method and call new T():
public static T GetCachedBoxedValue<T>(string cacheKey, Func<T> getFromExternFunction, bool skipLocalCaches = false)
where T:new()
{
...
if (r == null || r.Item1 == null)
{
return new T();
}
return r.Item1;
}
The new constraint tells the debugger that only types that have a parameterless constructor can be used, allowing you to call it.
The default value for all reference types is null. An empty List on the other hand is a fully constructed List instance. For more information about default values see: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/default-values
Related
I'm trying to create a type based on a string parameter and pass that into the type parameter of a constructor. It get's pretty nasty when just checking it with if-statements and I don't know how to do it more programmatically / generically.
I have tried with reflection but that only returns an object and passing an object to < T > is obviously not working.
Does anyone have an idea how to solve this in a more deliate way without the thousands of if statements?
Object creation looks like this:
if (Options.Input1Type == "int" && Options.Output1Type == "int") return BlockBuilder.Build<int, int>(Kind, Options, TransformToSelf);
if (Options.Input1Type == "bool" && Options.Output1Type == "bool") return BlockBuilder.Build<bool, bool>(Kind, Options, TransformToSelf);
if (Options.Input1Type == "string" && Options.Output1Type == "string") return BlockBuilder.Build<string, string>(Kind, Options, TransformToSelf);
if (Options.Input1Type == "bool" && Options.Output1Type == "int") return BlockBuilder.Build<bool, int>(Kind, Options, TransformToInt);
if (Options.Input1Type == "bool" && Options.Output1Type == "string") return BlockBuilder.Build<bool, string>(Kind, Options, TransformToString);
if (Options.Input1Type == "int" && Options.Output1Type == "bool") return BlockBuilder.Build<int, bool>(Kind, Options, TransformToBool);
if (Options.Input1Type == "int" && Options.Output1Type == "string") return BlockBuilder.Build<int, string>(Kind, Options, TransformToString);
if (Options.Input1Type == "string" && Options.Output1Type == "int") return BlockBuilder.Build<string, int>(Kind, Options, TransformToInt);
if (Options.Input1Type == "string" && Options.Output1Type == "bool") return BlockBuilder.Build<string, bool>(Kind, Options, TransformToBool);
BlockBuilder looks like this:
public static IDataflowBlock Build<TIn, TOut>(string kind, BlockOptions blockOptions, Func<TIn, TOut> singleOutputExecutionFunction = null, Func<TIn, IEnumerable<TOut>> multipleOutputExecutionFunction = null)
{
if (singleOutputExecutionFunction == null && multipleOutputExecutionFunction == null)
throw new ArgumentException("Missing function to execute");
Enum.TryParse(kind, out TransformationBlocks Kind);
switch (Kind)
{
case TransformationBlocks.Undefined:
throw new ArgumentException("No block type was specified");
case TransformationBlocks.TransformBlock:
return new TransformBlock<TIn, TOut>(param => { return singleOutputExecutionFunction(param); }, new ExecutionDataflowBlockOptions()
{
MaxMessagesPerTask = blockOptions.MaxMessagesPerTask,
BoundedCapacity = blockOptions.BoundedCapacity,
MaxDegreeOfParallelism = blockOptions.MaxDegreeOfParallelism,
});
case TransformationBlocks.TransformManyBlock:
return new TransformManyBlock<TIn, TOut>(param => { return multipleOutputExecutionFunction(param); }, new ExecutionDataflowBlockOptions()
{
MaxMessagesPerTask = blockOptions.MaxMessagesPerTask,
BoundedCapacity = blockOptions.BoundedCapacity,
MaxDegreeOfParallelism = blockOptions.MaxDegreeOfParallelism,
});
default:
return default;
}
}
And the delegates / functions looks like this:
private static T TransformToSelf<T>(T obj)
{
return obj;
}
private static string TransformToString<T>(T obj)
{
return Convert.ToString(obj);
}
private static int TransformToInt<T>(T obj)
{
return Convert.ToInt32(obj);
}
private static bool TransformToBool<T>(T obj)
{
return Convert.ToBoolean(obj);
}
It's not easy but it's doable.
If you can change the type of Input1Type and Input2Type to be System.Type rather than string, it's much easier.
If not, then I would suggest you create a mapping function as susggested by #neil that maps strings to types, then use MethodInfo.MakeGenericType() to call your Build() function.
See below for a simple example of MakeGenericType().
using System;
using System.Reflection;
namespace make_generic_type
{
class Program
{
static void Main(string[] args)
{
// Normal C# usage
var host = new Host();
Console.WriteLine(host.GenericMethod<int, string>("Test"));
// Use reflection to get type definition
var unboundMethod = typeof(Host).GetMethod(nameof(Host.GenericMethod));
// As the method is generic, you need to pass the type parameters in.
// We do this by binding the type parameters with MethodInfo.MakeGenericMethod();
var boundMethod = unboundMethod.MakeGenericMethod(new Type[]{ typeof(int), typeof(string) });
// Now we have a method that we can invoke via reflection as normal
Console.WriteLine(boundMethod.Invoke(new Host(), new object[]{ "Test"}));
}
}
class Host{
public string GenericMethod<TIn, TOut>(string kind)
{
return $"{typeof(TIn).Name}; {typeof(TOut).Name}; {kind};";
}
}
}
I'm creating a function to loop over an object and its childs.
But I'm having some issue when i try to map from the original object to the new object, here is the code:
public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{
try
{
var objectroutelist = objectRoute.Split('.');
var objroute = objectroutelist.First();
var childproperty = typeof(T).GetProperty(objroute);
if (objectroutelist.Count() == 1)
{
if (childproperty.GetValue(obj).ToString().Trim() == value)
{
return true;
}
return false;
}
else
{
var instance = Activator.CreateInstance(childproperty.PropertyType);
//childproperty.SetValue(obj, instance, null);
//childproperty.SetValue(instance, obj, null);
instance.MapValues(childproperty);
instance.MatchObjectField(string.Join(".", objectroutelist.Skip(1)), value);
}
}
catch (Exception e)
{
return false;
}
return false;
}
And here the class where I do the map and contains the issue.
public static void MapValues<T>(this T destination, PropertyInfo orgproperty)
{
var orgvalues = orgproperty.GetPropertiesWithValues();
var instance = Activator.CreateInstance(typeof(T));
foreach (var property in (typeof(T)).GetProperties())
{
try
{
var value = orgvalues.FirstOrDefault(a => a.Key == property.Name);
property.SetValue(instance, value);
}
catch (Exception)
{
property.SetValue(instance, null);
}
}
destination = (T)(object)instance;
}
The idea of the function is call with objectName.MatchObjectField("parent.child.child.child","MyName")
When I try to compare a field in the parent like objectName.MatchObjectField("Country","Ireland") it works perfectly
But when I try to create the child structure doing a call like objectName.MatchObjectField("Country.Address.City","Dublin") it breaks when I try to map to the new object.
What I noticed is that the property destination into the method MapValues<T> is mapped as Country.Address with all the properties as null which is correct.
But (typeof(T)).GetProperties() doesn't return anything.
Also i noticed that Activator.CreateInstance(typeof(T)) retunrs type {object} instead of return Country.Address
that may be the reason why is not working.
Any idea how to fix this?
EDIT: add get properties with values-> it retuns a Dictionary<string, object>
public static Dictionary<string, object> GetPropertiesWithValues(this Object obj, bool includeValueTypes = true)
{
return InternalGetProperties(obj, false, includeValueTypes);
}
private static Dictionary<string, object> InternalGetProperties(Object obj, bool withDefaultValue, bool includeValueTypes = true)
{
Dictionary<string, object> d = new Dictionary<string, object>();
var res = GetPropertiesR(obj, d, "", withDefaultValue, includeValueTypes);
return res;
}
private static Dictionary<string, object> GetPropertiesR(Object obj, Dictionary<string, object> d, string parent, bool searchingForDefaults, bool includeValueTypes)
{
if (obj == null)
return d;
var pis = #obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (pis == null || pis.Length == 0)
throw new InvalidOperationException("This object doens't have inner properties");
Func<string, string> formatProperty = (property) => string.Concat(parent, parent == "" ? "" : ".", property);
foreach (var pi in pis)
{
object data = pi.GetValue(#obj, null);
// check if is value type
if (pi.PropertyType.IsValueType)
{
// data is never null
if (!includeValueTypes)
continue;
Type nullableType = Nullable.GetUnderlyingType(pi.PropertyType);
object defaultValue = nullableType != null ? GetDefault(nullableType) : GetDefault(data.GetType());
if (!searchingForDefaults)
{
// check default values.
if (data != null && data.ToString() != defaultValue.ToString())
d.Add(formatProperty(pi.Name), data);
}
else
{
// check default values
if ((nullableType != null && data == null) || data.ToString() == defaultValue.ToString())
d.Add(formatProperty(pi.Name), data);
}
}
else
{
//
// reference types
if (!searchingForDefaults)
{
if (data == default(object))
continue;
if (IsThisPropertyPartofSystemNamespace(pi))
{
// transform list into a string with values.
IEnumerable enumeration = data as IList;
if (enumeration != null)
{
StringBuilder sb = new StringBuilder();
foreach (var i in enumeration)
sb.Append(string.Concat(i.ToString(), ", "));
if (sb.Length >= 2)
sb.Remove(sb.Length - 2, 2);
data = sb.ToString();
}
d.Add(formatProperty(pi.Name), data);
}
else
{
//
// user complex type defined
string ctxParent = string.Concat(parent, parent == "" ? "" : ".", pi.Name);
GetPropertiesR(data, d, ctxParent, searchingForDefaults, includeValueTypes);
}
}
else
{
if (data != default(object))
continue;
d.Add(formatProperty(pi.Name), data);
}
}
}
return d;
}
private static bool IsThisPropertyPartofSystemNamespace(PropertyInfo pi)
{
var systemNames = new HashSet<string>
{
"mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}",
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}"
};
var isSystemType = systemNames.Contains(pi.PropertyType.Assembly.FullName);
return isSystemType;
}
private static object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
TL;DR: Take a step back. Describe the responsibility of each of the methods separately, and write tests for them that way. Start with the "lowest level" method and work your way up. That will make it a lot easier to see what's going wrong.
There are multiple problems here. First look at these two lines of code:
var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues(childproperty);
The return type of Activator.CreateInstance is object, so this is effectively:
var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues<object>(childproperty);
That's not what you want - you want to use childproperty.PropertyType as the type argument to MapValues. You don't know that at compile-time, so it's not a good fit as a type parameter.
But beyond that, your MapValues method has a bigger problem: it basically ignores its parameter. The only time it uses destination is in the last line, when it assigns a new value to it:
destination = (T)(object)instance;
That parameter is a value parameter, so assigning it at the end of the method is pointless.
You should decide what the purpose of MapValues is:
Is it to create an instance and populate it, then return it?
Is it to accept an existing instance and populate that?
Either is simple enough to implement, but at the moment you're half way between the two. Also note that you're only passing in a single PropertyInfo - consider how you'd expect to assign multiple properties with that.
Finally, there's the matter of where the values come from. You're currently calling GetPropertiesWithValues on the PropertyInfo - that's not going to do what I think you expect it to. You need to provide the source object itself, otherwise there's nowhere to get values from.
I found what i need to do in this post: Getting Nested Object Property Value Using Reflection
getting the nested object is an easy way of what i was pretending to do.
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if(propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{
try
{
var propvalue = GetPropertyValue(obj, objectRoute);
return ( propvalue.ToString().Trim() == value.Trim());
}
catch (Exception) {
throw;
}
}
I wrote below method with follwing requirement -
input is xmlnode and attributeName
return the value if it is found with the associated attribute name passed
Where there is no value in attributeName being passed, it should return -
3.1. For int -1
3.2. For Datetime DateTime.MinValue
3.3. For String, null
3.4. For bool, null
Below method fails for case 3.4.
public T AttributeValue<T>(XmlNode node, string attributeName)
{
var value = new object();
if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
{
value = node.Attributes[attributeName].Value;
}
else
{
if (typeof(T) == typeof(int))
value = -1;
else if (typeof(T) == typeof(DateTime))
value = DateTime.MinValue;
else if (typeof(T) == typeof(string))
value = null;
else if (typeof(T) == typeof(bool))
value = null;
}
return (T)Convert.ChangeType(value, typeof(T));
}
When change this to
public System.Nullable<T> AttributeValue<T>(XmlNode node, string attributeName) where T : struct
{
var value = new object();
if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
{
value = node.Attributes[attributeName].Value;
}
else
{
if (typeof(T) == typeof(int))
value = -1;
else if (typeof(T) == typeof(DateTime))
value = DateTime.MinValue;
else if (typeof(T) == typeof(string))
return null;
else if (typeof(T) == typeof(bool))
return null;
}
return (T?)Convert.ChangeType(value, typeof(T));
}
It fails for string type i.e. case 3.3
Looking forward for some help.
For 3.4 you need to use bool? as the type for T, so you can return null.
Then you can use the default keyword for 3.3 and 3.4 (string and bool?). As per msdn it will return null for reference types and the default value for value types (like int or bool).
You can use it like
return default(T);
thanks for a number of replies, this is what I wrote and it works for me..
It returns null for the types.
public T AttributeValue<T>(XmlNode node, string attributeName)
{
object value = null;
if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
value = node.Attributes[attributeName].Value;
if (typeof(T) == typeof(bool?) && value != null)
value = (string.Compare(value.ToString(), "1", true) == 0).ToString();
var t = typeof(T);
t = Nullable.GetUnderlyingType(t) ?? t;
return (value == null) ?
default(T) : (T)Convert.ChangeType(value, t);
}
I call it like this
const string auditData = "<mydata><data><equipmentStatiticsData><userStatistics maxUsers='100' totalUsers='1' authUsers='0' pendingUsers='' adminAddedUsers='' xmlUsers='' internalDBUsers='' webUsers='' macUsers='' vpnUsers='' xUsers8021=''></userStatistics><equipmentStatistics cpuUseNow='14' cpuUse5Sec='1' cpuUse10Sec='1' cpuUse20Sec='1' ramTotal='31301632' ramUtilization ='1896448' ramBuffer='774144' ramCached='8269824' permStorageUse='24' tempStorageUse='24'></equipmentStatistics><authStatus status='1'></authStatus></equipmentStatiticsData></data></mydata>";
xmlDoc.LoadXml(auditData);
var userStatsNode = xmlDoc.SelectSingleNode("/mydata/data/equipmentStatiticsData/userStatistics");
var intNullable = AttributeValue<int?>(userStatsNode, "vpnUsers");
var nullableBoolTrue = AttributeValue<bool?>(userStatsNode, "totalUsers");
var nullableBoolFalse = AttributeValue<bool?>(userStatsNode, "authUsers");
var nullableString = AttributeValue<string>(userStatsNode, "authUsers");
var pendingUsersBoolNull = AttributeValue<bool?>(userStatsNode, "pendingUsers");
var testAttribNullableNotFoundDateTime = AttributeValue<DateTime?>(userStatsNode, "testAttrib");
var testAttrib1NullString = AttributeValue<string>(userStatsNode, "testAttrib");
var maxUsersNullInt = AttributeValue<int?>(userStatsNode, "maxUsers");
it works well for me. thanks people...
You need to call your first code set using bool? not bool because null isn't a valid value for a non-nullable bool.
Your second code block fails because you can't use string for the generic type of Nullable<T> as it requires a value-type struct and string is a reference-type class.
You'll need to change your first method block to look for typeof(bool?) and call it with a nullable boolean type:
public T AttributeValue<T>(XmlNode node, string attributeName)
{
var value = new object();
if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
{
value = node.Attributes[attributeName].Value;
}
else
{
if (typeof(T) == typeof(int))
value = -1;
else if (typeof(T) == typeof(DateTime))
value = DateTime.MinValue;
else if (typeof(T) == typeof(string))
value = null;
else if (typeof(T) == typeof(bool?))
value = null;
}
var type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
return (T)Convert.ChangeType(value, type);
}
Then call it as:
bool? value = AttributeValue<bool?>(node, "myAttributeName");
You also need to do a check as Convert.ChangeType won't work for a nullable type. A quick fix from here resolves that. (it is included in the code above)
EDIT: Here's an improved/cleaned version of your method:
public T AttributeValue<T>(XmlNode node, string attributeName)
{
if (node.Attributes[attributeName] != null && !string.IsNullOrEmpty(node.Attributes[attributeName].Value))
{
var value = node.Attributes[attributeName].Value;
var type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
return (T)Convert.ChangeType(value, type);
}
else
{
if (typeof(T) == typeof(int))
return (T)(object)(-1);
return default(T);
}
}
You can add additional special cases for non-existing nodes, but all your cases except for int are already the default value for the types so just use default(T) instead.
I have this simple method:
#region Fields
private Collection<Address> _addresses;
#endregion
#region Public methods
public Address DeliveryAddress()
{
if (_addresses == null)
if (this.Id > 0)
_addresses = Core.Data.Addresses.GetClient(this.Id);
return _addresses.SingleOrDefault(x => x.TypeId == AddressType.Delivery);
}
public Address InvoiceAddress()
{
if (_addresses == null)
if (this.Id > 0)
_addresses = Core.Data.Addresses.GetClient(this.Id);
return _addresses.SingleOrDefault(x => x.TypeId == AddressType.Invoice);
}
#endregion
As you can see I trying to return one result for a DeliveryAddress and one result for an InvoiceAddress. My problem is that I would like the link expression to create a new instance of Address() if SingleOrDefault returns null.
I am really new to linq, so I am not sure whether SingleOrDefault is the correct expression I should be using.
You could use DefaultIfEmpty and use that instance as default value:
return _addresses.Where(x => x.TypeId == AddressType.Delivery)
.DefaultIfEmpty(new Adress())
.Single();
Use the null-coalescing operator:
return _addresses
.SingleOrDefault(x => x.TypeId == AddressType.Delivery) ?? new Address();
The expression
x ?? y
yields x if x is not null, otherwise y. You can chain the operator
x ?? y ?? z ?? t
This returns the first non-null value or null if all of them are null.
UPDATE
Note that SingleOrDefault throws an exception if the sequence has more than one element. If you need the first element of a sequence possibly having no or more than one element, use FirstOrDefault instead.
You could create your own extension method, like this:
public static T NewIfNull<T>(this T obj) where T: class, new()
{
return obj ?? new T();
}
... then tack a usage onto the end of SingleOrDefault:
var singleResult = myCollection.SingleOrDefault().NewIfNull();
... or because the logic is so simple, just inline it as other answers have said.
Instead of
return _addresses.SingleOrDefault(x => x.TypeId == AddressType.Delivery);
Do something like this:
var address = _addresses.SingleOrDefault(x => x.TypeId == AddressType.Delivery);
if(address == null)
address = new Address();
return address;
I'd be inclined to write both of these as extension methods on IEnumerable<Address>. You can use the null-coalesing operator to return a new instance if the SingleOrDefault() call returns null.
public static class AddressExtensions
{
public static Address DeliveryAddress(this IEnumerable<Address> addresses)
{
return addresses.SingleOrDefault(x => x.TypeId == AddressType.Delivery)
?? new Address();
}
public static Address InvoiceAddress(this IEnumerable<Address> addresses)
{
return addresses.SingleOrDefault(x => x.TypeId == AddressType.Invoice)
?? new Address();
}
}
Apart from alternatives in other answers, you can also create your own SingleOrNew Extension method.
public static TSource SingleOrNew<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate ) where T:new()
{
return source.SingleOrDefault(predicate) ?? new T();
}
It can be used as
return _addresses.SingleOrNew(x => x.TypeId == AddressType.Delivery);
I have someone on GitHub asking for the ability to compare HashSets for my project on GitHub: https://github.com/GregFinzer/Compare-Net-Objects/. I need to be able to determine if a type is a hash set and then get the enumerator. I am not sure what to cast it to. Here is what I have for an IList:
private bool IsIList(Type type)
{
return (typeof(IList).IsAssignableFrom(type));
}
private void CompareIList(object object1, object object2, string breadCrumb)
{
IList ilist1 = object1 as IList;
IList ilist2 = object2 as IList;
if (ilist1 == null) //This should never happen, null check happens one level up
throw new ArgumentNullException("object1");
if (ilist2 == null) //This should never happen, null check happens one level up
throw new ArgumentNullException("object2");
try
{
_parents.Add(object1);
_parents.Add(object2);
//Objects must be the same length
if (ilist1.Count != ilist2.Count)
{
Differences.Add(string.Format("object1{0}.Count != object2{0}.Count ({1},{2})", breadCrumb,
ilist1.Count, ilist2.Count));
if (Differences.Count >= MaxDifferences)
return;
}
IEnumerator enumerator1 = ilist1.GetEnumerator();
IEnumerator enumerator2 = ilist2.GetEnumerator();
int count = 0;
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
string currentBreadCrumb = AddBreadCrumb(breadCrumb, string.Empty, string.Empty, count);
Compare(enumerator1.Current, enumerator2.Current, currentBreadCrumb);
if (Differences.Count >= MaxDifferences)
return;
count++;
}
}
finally
{
_parents.Remove(object1);
_parents.Remove(object2);
}
}
I believe it is enough works directly with the ISet<T>, the ICollection<T> or the IEnumerable<T> generic interfaces instead of the HashSet<T>. You can detect these types using the following approach:
// ...
Type t = typeof(HashSet<int>);
bool test1 = GenericClassifier.IsICollection(t); // true
bool test2 = GenericClassifier.IsIEnumerable(t); // true
bool test3 = GenericClassifier.IsISet(t); // true
}
//
public static class GenericClassifier {
public static bool IsICollection(Type type) {
return Array.Exists(type.GetInterfaces(), IsGenericCollectionType);
}
public static bool IsIEnumerable(Type type) {
return Array.Exists(type.GetInterfaces(), IsGenericEnumerableType);
}
public static bool IsISet(Type type) {
return Array.Exists(type.GetInterfaces(), IsGenericSetType);
}
static bool IsGenericCollectionType(Type type) {
return type.IsGenericType && (typeof(ICollection<>) == type.GetGenericTypeDefinition());
}
static bool IsGenericEnumerableType(Type type) {
return type.IsGenericType && (typeof(IEnumerable<>) == type.GetGenericTypeDefinition());
}
static bool IsGenericSetType(Type type) {
return type.IsGenericType && (typeof(ISet<>) == type.GetGenericTypeDefinition());
}
}
You need to loop over GetInterfaces() and check whether it implements an interface where IsGenericType is true and GetGenericTypeDefinition() == typeof(ISet<>)
Accepted answer does not distinguish from Dictionary type and possibly other subclasses of ICollection and IEnumerable. This works better:
Type t1 = typeof(HashSet<int>);
bool test1 = t1.IsGenericType &&
t1.GetGenericTypeDefinition() == typeof(HashSet<>); // true
Type t2 = typeof(Dictionary<int, string>);
bool test2 = t2.IsGenericType &&
t2.GetGenericTypeDefinition() == typeof(HashSet<>); // false
Type t3 = typeof(int);
bool test3 = t3.IsGenericType &&
t3.GetGenericTypeDefinition() == typeof(HashSet<>); // false
This is built in to HashSets... Use the method SymmetricExceptWith. There are other built in comparisons as well. See: http://msdn.microsoft.com/en-us/library/bb336848.aspx