C# - Cast to list takes up 4.5 sec - only 76 items in collection - c#

I've got a collection (languages). In this collection are 76 items. I want to cast this collection to a list with languages.Cast<LanguageTextValue>().ToList();. Actually it takes around 4.5 sec. to take this step. Can anyone tell me, how I can have a further look, why this steps takes so long or show me a method to cast this collection to a list in a quicker way?
LanguageTextValueCollection languages = new LanguageTextValueCollection(LanguageTextValueKind.LanguageNo, new object[] { languageNo });
var languageList = languages.Cast<LanguageTextValue>().ToList();
The data of this collection is stored in a DataBuffer. So loading the data from the database is not the reason why it takes so long. It is really the step casting the collection to a list.
This is the code of the collection:
using System;
using System.Collections;
using System.ComponentModel;
namespace Library.Languages
{
#region Enums
public enum LanguageTextValueKind
{
All,
LanguageNo
}
#endregion Enums
public class LanguageTextValueCollection : CommonObjectCollection
{
#region DataBuffer
public static System.Data.DataTable DataBuffer = new LanguageTextValueCollection(LanguageTextValueKind.All, null).DataSource;
#endregion DataBuffer
#region Properties
public override Library.CommonObject this[int Index]
{
get
{
return new LanguageTextValue(new object[] { this.DataSource.Rows[Index]["LanguageNo"].ToString(), this.DataSource.Rows[Index]["LanguageTextTypeID"].ToString() });
}
}
public new LanguageTextValue this[string SearchExpression]
{
get
{
System.Data.DataRow[] rows = base[SearchExpression];
if (rows.Length > 0)
{
LanguageTextValue temp = new LanguageTextValue(new object[] { rows[0]["LanguageNo"].ToString(), rows[0]["LanguageTextTypeID"].ToString() });
if (temp.RequestResult.State == ResultState.Success)
return temp;
else
return null;
}
else
{
rows = LanguageTextValueCollection.DataBuffer.Select(SearchExpression + " AND LanguageNo=9");
if (rows.Length > 0)
{
LanguageTextValue temp = new LanguageTextValue(new object[] { rows[0]["LanguageNo"].ToString(), rows[0]["LanguageTextTypeID"].ToString() });
if (temp.RequestResult.State == ResultState.Success)
return temp;
else
return null;
}
else
return null;
}
}
}
#endregion Properties
#region Constructors
public LanguageTextValueCollection()
{
}
public LanguageTextValueCollection(LanguageTextValueKind KindOfObject, object[] Value)
{
this.DataGet(KindOfObject, Value);
}
#endregion Constructors
#region Data Methods
public Result DataGet(LanguageTextValueKind kindOfObject, object[] value)
{
try
{
if (LanguageTextValueCollection.DataBuffer == null)
{
this.FillParameters(System.Data.CommandType.StoredProcedure, "sp_tbl_LanguageText_Value_Get_By_All", null, null);
this.DBDataGet(this.CommandObject);
if (this.RequestResult.State == ResultState.Failure)
throw new Exception(this.RequestResult.Message);
LanguageTextValueCollection.DataBuffer = this.DataSource.Copy();
}
this.DataSource = LanguageTextValueCollection.DataBuffer.Clone();
switch (kindOfObject)
{
case LanguageTextValueKind.All:
this.DataSource = LanguageTextValueCollection.DataBuffer.Copy();
break;
case LanguageTextValueKind.LanguageNo:
System.Data.DataRow[] rows = LanguageTextValueCollection.DataBuffer.Select("LanguageNo = " + value[0].ToString());
foreach (System.Data.DataRow row in rows)
this.DataSource.Rows.Add(row.ItemArray);
break;
}
if (this.RequestResult.State == ResultState.Success)
this.Count = this.DataSource.Rows.Count;
}
catch (Exception ex)
{
this.RequestResult = new Result(ex);
}
return this.RequestResult;
}
protected new Result FillParameters(System.Data.CommandType CommandType, string CommandText, string[] ColumnName, object[] Parameter)
{
try
{
this.CommandObject = new System.Data.SqlClient.SqlCommand(CommandText);
this.CommandObject.CommandType = CommandType;
if (Parameter != null)
for (int i = 0; i < ColumnName.Length; i++)
this.CommandObject.Parameters.Add(new System.Data.SqlClient.SqlParameter("#" + ColumnName[i], Parameter[i]));
}
catch (Exception ex)
{
this.RequestResult = new Result(ex);
}
return this.RequestResult;
}
#endregion Data Methods
}
}

Related

De-serializing a flagged enum with a space results in SerializationException

When de-serializing a flagged enum that is decorated with a EnumMemberAttribute with a value containing a space a SerializationException is thrown. The space in the value is treated as a separator.
Is there a way to change the separator or put the values in quotes ? Or is there even a more simple solution ?
Options I already am considering are :
Replacing the flagged enum with a list of this enum type
Replacing the spaces with underscores
This is used in a WCF service, and I am
aware that enums in datacontracts by some are considered a bad thing.
So I am also thinking about losing the enum’s all together.
But I really feel that this should be something configurable or something other people already solved. But I can't find anything.
I have boiled the problem down to a simple unit test. The code below results in:
Message=Invalid enum value 'Test' cannot be deserialized into type 'UnitTests.TestEnum'. Ensure that the necessary enum values are present and are marked with EnumMemberAttribute attribute if the type has DataContractAttribute attribute.
Source=System.Runtime.Serialization
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests
{
[TestClass]
public class EnumSerizalizationTests
{
[TestMethod]
public void SerializingAndDesrializingAFlaggedEnumShouldResultInSameEnumValues()
{
//Arrange
var orgObject = new TestClass { Value = TestEnum.TestValue1 | TestEnum.TestValue2 };
//Act
var temp = DataContractSerializeObject(orgObject);
var newObject = DataContractDeSerializeObject<TestClass>(temp);
//Assert
newObject.ShouldBeEquivalentTo(orgObject, "Roundtripping serialization should result in same value");
}
public string DataContractSerializeObject<T>(T objectToSerialize)
{
using (var output = new StringWriter())
{
using (var writer = new XmlTextWriter(output) {Formatting = Formatting.Indented})
{
new DataContractSerializer(typeof (T)).WriteObject(writer, objectToSerialize);
return output.GetStringBuilder().ToString();
}
}
}
public T DataContractDeSerializeObject<T>(string stringToDeSerialize)
{
DataContractSerializer ser = new DataContractSerializer(typeof(T));
T result;
using (StringReader stringReader = new StringReader(stringToDeSerialize))
{
using (XmlReader xmlReader = XmlReader.Create(stringReader))
{
result = (T)ser.ReadObject(xmlReader);
}
}
return result;
}
}
[DataContract]
[KnownType(typeof(TestEnum))]
public class TestClass
{
[DataMember]
public TestEnum Value { get; set; }
}
[Flags]
[DataContract]
public enum TestEnum
{
[EnumMember(Value = "Test value one")]
TestValue1 = 1,
[EnumMember(Value = "Test value two")]
TestValue2 = 2,
[EnumMember]
TestValue3 = 4,
[EnumMember]
TestValue4 = 8,
}
}
You can't use space in values because DataContractSerializer uses it and it is hardcoded. See the source and the post. But if you really want to use space between words, then use one of the listed solutions:
The first way. Use other whitespace characters such as three-per-em space in values. But you will have another problem - there is no visual separator between values.
<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication">
<Value>Test value one Test value two</Value>
</TestClass>
The second way is to use IDataContractSurrogate. This way will produce the XML listed below:
<TestClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication">
<Value i:type="EnumValueOfTestEnum9cBcd6LT">Test value one, Test value two</Value>
</TestClass>
How does it work? We will just wrap our enumeration in the process of serialization and unwrap in case of deserialization. In order to do that we should use IDataContractSurrogate:
new DataContractSerializerSettings()
{
DataContractSurrogate = new EnumSurrogate(),
KnownTypes = new Type[] { typeof(EnumValue<TestEnum>) }
};
public class EnumSurrogate : IDataContractSurrogate
{
#region IDataContractSurrogate Members
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
return null;
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
return null;
}
public Type GetDataContractType(Type type)
{
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
IEnumValue enumValue = obj as IEnumValue;
if (enumValue!= null)
{ return enumValue.Value; }
return obj;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj != null)
{
Type type = obj.GetType();
if (type.IsEnum && Attribute.IsDefined(type, typeof(FlagsAttribute)))
{ return Activator.CreateInstance(typeof(EnumValue<>).MakeGenericType(type), obj); }
}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
return null;
}
public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
{
return null;
}
#endregion
}
public interface IEnumValue : IXmlSerializable
{
object Value { get; }
}
[Serializable]
public class EnumValue<T> : IEnumValue
where T : struct
{
#region Fields
private Enum value;
private static Type enumType;
private static long[] values;
private static string[] names;
private static bool isULong;
#endregion
#region Constructors
static EnumValue()
{
enumType = typeof(T);
if (!enumType.IsEnum)
{ throw new InvalidOperationException(); }
FieldInfo[] fieldInfos = enumType.GetFields(BindingFlags.Static | BindingFlags.Public);
values = new long[fieldInfos.Length];
names = new string[fieldInfos.Length];
isULong = Enum.GetUnderlyingType(enumType) == typeof(ulong);
for (int i = 0; i < fieldInfos.Length; i++)
{
FieldInfo fieldInfo = fieldInfos[i];
EnumMemberAttribute enumMemberAttribute = (EnumMemberAttribute)fieldInfo
.GetCustomAttributes(typeof(EnumMemberAttribute), false)
.FirstOrDefault();
IConvertible value = (IConvertible)fieldInfo.GetValue(null);
values[i] = (isULong)
? (long)value.ToUInt64(null)
: value.ToInt64(null);
names[i] = (enumMemberAttribute == null || string.IsNullOrEmpty(enumMemberAttribute.Value))
? fieldInfo.Name
: enumMemberAttribute.Value;
}
}
public EnumValue()
{
}
public EnumValue(Enum value)
{
this.value = value;
}
#endregion
#region IXmlSerializable Members
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
string stringValue = reader.ReadElementContentAsString();
long longValue = 0;
int i = 0;
// Skip initial spaces
for (; i < stringValue.Length && stringValue[i] == ' '; i++) ;
// Read comma-delimited values
int startIndex = i;
int nonSpaceIndex = i;
int count = 0;
for (; i < stringValue.Length; i++)
{
if (stringValue[i] == ',')
{
count = nonSpaceIndex - startIndex + 1;
if (count > 1)
{ longValue |= ReadEnumValue(stringValue, startIndex, count); }
nonSpaceIndex = ++i;
// Skip spaces
for (; i < stringValue.Length && stringValue[i] == ' '; i++) ;
startIndex = i;
if (i == stringValue.Length)
{ break; }
}
else
{
if (stringValue[i] != ' ')
{ nonSpaceIndex = i; }
}
}
count = nonSpaceIndex - startIndex + 1;
if (count > 1)
longValue |= ReadEnumValue(stringValue, startIndex, count);
value = (isULong)
? (Enum)Enum.ToObject(enumType, (ulong)longValue)
: (Enum)Enum.ToObject(enumType, longValue);
}
public void WriteXml(XmlWriter writer)
{
long longValue = (isULong)
? (long)((IConvertible)value).ToUInt64(null)
: ((IConvertible)value).ToInt64(null);
int zeroIndex = -1;
bool noneWritten = true;
for (int i = 0; i < values.Length; i++)
{
long current = values[i];
if (current == 0)
{
zeroIndex = i;
continue;
}
if (longValue == 0)
{ break; }
if ((current & longValue) == current)
{
if (noneWritten)
{ noneWritten = false; }
else
{ writer.WriteString(","); }
writer.WriteString(names[i]);
longValue &= ~current;
}
}
if (longValue != 0)
{ throw new InvalidOperationException(); }
if (noneWritten && zeroIndex >= 0)
{ writer.WriteString(names[zeroIndex]); }
}
#endregion
#region IEnumValue Members
public object Value
{
get { return value; }
}
#endregion
#region Private Methods
private static long ReadEnumValue(string value, int index, int count)
{
for (int i = 0; i < names.Length; i++)
{
string name = names[i];
if (count == name.Length && string.CompareOrdinal(value, index, name, 0, count) == 0)
{ return values[i]; }
}
throw new InvalidOperationException();
}
#endregion
}
The third way is to emit dynamically the class, if base class has flagged Enum properties, replace them with string properties and use instances of the generated class as surrogates.

How to get dynamic data from ADO.NET in while loop

ASP.NET MVC4 application uses modified WebMatrix Dynamicrecord to get dynamic data from ADO.NET and show it in a WebGrid.
Running application causes strange exception
Invalid attempt to read when no data is present
in method
private object GetNonNullValue(int i)
at line
var value = Record[i];
Using foreach as shown in comment does not work in Mono as discussed in https://github.com/npgsql/npgsql/issues/295 . So application uses while but while does not work in Windows also.
How to get dynamic data in while loop ?
Whole solution is available at http://wikisend.com/download/360760/invalidattemptoreadwhennodataispresent.zip
Controller:
using Eeva.Business;
using Eeva.Erp.ViewModels;
using Npgsql;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Web.Mvc;
namespace Eeva.Erp.Controllers
{
public class ReportController : Controller
{
public ActionResult Test()
{
var data = TestData();
return View("ReportData", new ReportDataViewModel(data, ""));
}
IEnumerable<dynamic> TestData()
{
using (var connection = new NpgsqlConnection(ConnectionString()))
{
connection.Open();
DbCommand command = (DbCommand)connection.CreateCommand();
command.CommandText = "select 'A' union select 'B'";
using (command)
{
using (DbDataReader reader = command.ExecuteReader())
{
IEnumerable<string> columnNames = null;
while (reader.Read())
{
if (columnNames == null)
columnNames = GetColumnNames(reader);
yield return new EevaDynamicRecord(columnNames, reader);
}
//foreach (DbDataRecord record in reader)
//{
// if (columnNames == null)
// columnNames = GetColumnNames(record);
// yield return new EevaDynamicRecord(columnNames, record);
//}
}
}
}
}
static IEnumerable<string> GetColumnNames(IDataRecord record)
{
// Get all of the column names for this query
for (int i = 0; i < record.FieldCount; i++)
yield return record.GetName(i);
}
static string ConnectionString()
{
return new NpgsqlConnectionStringBuilder()
{
Host = "localhost",
UserName = "postgres",
}.ConnectionString;
}
}
}
ViewModel:
using System.Collections.Generic;
using System.Web.Mvc;
using Eeva.Business;
namespace Eeva.Erp.ViewModels
{
public class ReportDataViewModel
{
public IEnumerable<dynamic> Rows { get; set; }
public string Source;
public ReportDataViewModel(IEnumerable<dynamic> rows, string source)
{
Rows = rows;
Source = source;
}
}
}
View:
#model Eeva.Erp.ViewModels.ReportDataViewModel
#using System.Web.Helpers
#{ Layout = null;
var gd = new WebGrid(source: Model.Rows );
}
<!DOCTYPE html>
<html>
<head></head>
<body>
#gd.GetHtml()
</body>
</html>
Dynamicrecord is used from MVC4 source code with modifications:
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Text;
using WebMatrix.Data.Resources;
namespace Eeva.Business
{
public sealed class EevaDynamicRecord : DynamicObject, ICustomTypeDescriptor
{
public EevaDynamicRecord(IEnumerable<string> columnNames, IDataRecord record)
{
Debug.Assert(record != null, "record should not be null");
Debug.Assert(columnNames != null, "columnNames should not be null");
Columns = columnNames.ToList();
Record = record;
}
public IList<string> Columns { get; private set; }
private IDataRecord Record { get; set; }
public object this[string name]
{
get
{
for (int i = 0; i < Record.FieldCount; i++)
{
string normname = Record.GetName(i);
if (normname.Equals(name, StringComparison.InvariantCultureIgnoreCase))
return GetNonNullValue(i);
}
throw new InvalidOperationException("No column " + name);
}
}
public object this[int index]
{
get
{
return GetNonNullValue(index); // GetValue(Record[index]);
}
}
public string Field(int fldNo)
{
return Record.GetName(fldNo).ToUpperInvariant();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this[binder.Name];
return true;
}
private object GetNonNullValue(int i)
{
var value = Record[i];
if (DBNull.Value == value || value == null)
{
var tt = Record.GetFieldType(i).Name;
switch (tt)
{
case "Decimal":
case "Int32":
case "Double":
return 0;
case "String":
return "";
case "DateTime":
return null;
case "Boolean":
// kui seda pole, siis demos lao kartoteek kartoteegi kaart annab vea:
return false;
}
return null;
}
if (value is decimal? || value is decimal)
return Convert.ChangeType(value, typeof(double));
if (value is string)
return value.ToString().TrimEnd();
return value;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return Columns;
}
private void VerifyColumn(string name)
{
// REVIEW: Perf
if (!Columns.Contains(name, StringComparer.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
"Invalid Column Name " + name));
}
}
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return AttributeCollection.Empty;
}
string ICustomTypeDescriptor.GetClassName()
{
return null;
}
string ICustomTypeDescriptor.GetComponentName()
{
return null;
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return null;
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return null;
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return null;
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return EventDescriptorCollection.Empty;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return EventDescriptorCollection.Empty;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
return ((ICustomTypeDescriptor)this).GetProperties();
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
// Get the name and type for each column name
var properties = from columnName in Columns
let columnIndex = Record.GetOrdinal(columnName)
let type = Record.GetFieldType(columnIndex)
select new DynamicPropertyDescriptor(columnName, type);
return new PropertyDescriptorCollection(properties.ToArray(), readOnly: true);
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
private class DynamicPropertyDescriptor : PropertyDescriptor
{
private static readonly Attribute[] _empty = new Attribute[0];
private readonly Type _type;
public DynamicPropertyDescriptor(string name, Type type)
: base(name, _empty)
{
_type = type;
}
public override Type ComponentType
{
get { return typeof(EevaDynamicRecord); }
}
public override bool IsReadOnly
{
get { return true; }
}
public override Type PropertyType
{
get { return _type; }
}
public override bool CanResetValue(object component)
{
return false;
}
public override object GetValue(object component)
{
EevaDynamicRecord record = component as EevaDynamicRecord;
// REVIEW: Should we throw if the wrong object was passed in?
if (record != null)
{
return record[Name];
}
return null;
}
public override void ResetValue(object component)
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
"DataResources.RecordIsReadOnly", Name));
}
public override void SetValue(object component, object value)
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
"DataResources.RecordIsReadOnly", Name));
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
}
}
I posted this also in http://forums.asp.net/p/2013821/5795169.aspx
Update
Problably creating a copy of every dynamicrecord solves the issue:
Dictionary<string, object> Clonedict;
public EevaDynamicRecord Clone()
{
var res = new EevaDynamicRecord(Columns, Record);
res.Clonedict = new Dictionary<string, object>();
for (int i = 0; i < Record.FieldCount; i++)
res.Clonedict[Record.GetName(i)] = Record[i]);
return res;
// this also does not work:
// return (EevaDynamicRecord)this.MemberwiseClone();
}
Is there better solution ?
I do not know that this will necessarily work, but to suggest it I needed to post an answer since it includes a code sample.
If I'm right and the problem is occurring because you're attempting to access data from a reader after the command has gone out of scope, this will work around that.
Replace your Test method with this:
public ActionResult Test()
{
var data = TestData().ToArray();
return View("ReportData", new ReportDataViewModel(data, ""));
}
If that doesn't help, try replacing the TestData method with this:
IEnumerable<dynamic> TestData()
{
List<dynamic> results = new List<dynamic>();
using (var connection = new NpgsqlConnection(ConnectionString()))
{
connection.Open();
DbCommand command = (DbCommand)connection.CreateCommand();
command.CommandText = "select 'A' union select 'B'";
using (command)
{
using (DbDataReader reader = command.ExecuteReader())
{
IEnumerable<string> columnNames = null;
while (reader.Read())
{
if (columnNames == null)
columnNames = GetColumnNames(reader);
results.Add(new EevaDynamicRecord(columnNames, reader));
}
//foreach (DbDataRecord record in reader)
//{
// if (columnNames == null)
// columnNames = GetColumnNames(record);
// yield return new EevaDynamicRecord(columnNames, record);
//}
}
}
}
return results;
}

How to get the Count property using reflection for Generic types

I have a list of objects, of which I cannot know the type of at compile-time.
I need to identify any of these objects where a 'Count' property exists, and get the value if it does.
This code works for simple Collection types:
PropertyInfo countProperty = objectValue.GetType().GetProperty("Count");
if (countProperty != null)
{
int count = (int)countProperty.GetValue(objectValue, null);
}
The problem is that this doesn't work for generic types, such as IDictionary<TKey,TValue>. In those cases, the 'countProperty' value is returned as null, even though a 'Count' property exists in the instanced object.
All I want to do is identify any collection/dictionary based object and find the size of it, if it has one.
Edit: as requested, here's the entire listing of code that doesn't work
private static void GetCacheCollectionValues(ref CacheItemInfo item, object cacheItemValue)
{
try
{
//look for a count property using reflection
PropertyInfo countProperty = cacheItemValue.GetType().GetProperty("Count");
if (countProperty != null)
{
int count = (int)countProperty.GetValue(cacheItemValue, null);
item.Count = count;
}
else
{
//poke around for a 'values' property
PropertyInfo valuesProperty = cacheItemValue.GetType().GetProperty("Values");
int valuesCount = -1;
if (valuesProperty != null)
{
object values = valuesProperty.GetValue(cacheItemValue, null);
if (values != null)
{
PropertyInfo valuesCountProperty = values.GetType().GetProperty("Count");
if (countProperty != null)
{
valuesCount = (int)valuesCountProperty.GetValue(cacheItemValue, null);
}
}
}
if (valuesCount > -1)
item.Count = valuesCount;
else
item.Count = -1;
}
}
catch (Exception ex)
{
item.Count = -1;
item.Message = "Exception on 'Count':" + ex.Message;
}
}
This works OK on simple collections, but not on an object created from a class I have which is derived from Dictionary<TKey,TValue>. Ie
CustomClass :
Dictionary<TKey,TValue>
CacheItemInfo is just a simple class that contains properties for cache items - ie, key, count, type, expiration datetime
The first thing you should try is casting to ICollection, as this has a very cheap .Count:
ICollection col = objectValue as ICollection;
if(col != null) return col.Count;
The Count for dictionary should work though - I've tested this with Dictionary<,> and it works fine - but note that even if something implements IDictionary<,>, the concrete type (returned via GetType()) doesn't have to have a .Count on the public API - it could use explicit interface implementation to satisfy the interface while not having a public int Count {get;}. Like I say: it works for Dictionary<,> - but not necessarily for every type.
As a last ditch effort if everything else fails:
IEnumerable enumerable = objectValue as IEnumerable;
if(enumerable != null)
{
int count = 0;
foreach(object val in enumerable) count++;
return count;
}
Edit to look into the Dictionary<,> question raised in comments:
using System;
using System.Collections;
using System.Collections.Generic;
public class CustomClass : Dictionary<int, int> { }
public class CacheItemInfo
{
public int Count { get; set; }
public string Message { get; set; }
}
class Program {
public static void Main() {
var cii = new CacheItemInfo();
var data = new CustomClass { { 1, 1 }, { 2, 2 }, { 3, 3 } };
GetCacheCollectionValues(ref cii, data);
Console.WriteLine(cii.Count); // expect 3
}
private static void GetCacheCollectionValues(ref CacheItemInfo item, object cacheItemValue)
{
try
{
ICollection col;
IEnumerable enumerable;
if (cacheItemValue == null)
{
item.Count = -1;
}
else if ((col = cacheItemValue as ICollection) != null)
{
item.Count = col.Count;
}
else if ((enumerable = cacheItemValue as IEnumerable) != null)
{
int count = 0;
foreach (object val in enumerable) count++;
item.Count = count;
}
else
{
item.Count = -1;
}
}
catch (Exception ex)
{
item.Count = -1;
item.Message = "Exception on 'Count':" + ex.Message;
}
}
}
How about adding this after your first check (!untested!) ...
foreach (Type interfaceType in objectValue.GetType().GetInterfaces())
{
countProperty = interfaceType.GetProperty("Count");
//etc.
}

C# implementation of deep/recursive object comparison in .net 3.5

I am looking for a C# specific , open source (or source code available) implementation of recursive, or deep, object comparison.
I currently have two graphs of live objects that I am looking to compare to each other, with the result of the comparison being a set of discrepancies in the graphs. The objects are instantiations of a set of classes that are known at run time (but not necessarily at compile time).
There is a specific requirement to be able to map from the discrepancies in the graphs, back to the objects containing the discrepancies.
I found a really nice, free implementation at www.kellermansoftware.com called Compare .NET Objects which can be found here. Highly recommended.
Appears to have relocated to github - most recent version is available here
This is a complex area; I've done some things like this at runtime, and it quickly gets messy. If possible, you might find that the simplest way to do this is to serialize the objects and compare the serialized form (perhaps xml-diff and XmlSerializer). This is complicated a little by the types not being known until runtime, but not hugely (you can always use new XmlSerializer(obj.GetType()) etc).
That would be my default approach, anyway.
Here's an NUnit 2.4.6 custom constraint we use for comparing complex graphs. It supports embedded collections, parent references, setting tolerance for numeric comparisons, identifying field names to ignore (even deep within the hierarchy), and decorating types to be always ignored.
I'm sure this code can be adapted to be used outside NUnit, the bulk of the code isn't dependent on NUnit.
We use this in thousands of unit tests.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using NUnit.Framework;
using NUnit.Framework.Constraints;
namespace Tests
{
public class ContentsEqualConstraint : Constraint
{
private readonly object expected;
private Constraint failedEquality;
private string expectedDescription;
private string actualDescription;
private readonly Stack<string> typePath = new Stack<string>();
private string typePathExpanded;
private readonly HashSet<string> _ignoredNames = new HashSet<string>();
private readonly HashSet<Type> _ignoredTypes = new HashSet<Type>();
private readonly LinkedList<Type> _ignoredInterfaces = new LinkedList<Type>();
private readonly LinkedList<string> _ignoredSuffixes = new LinkedList<string>();
private readonly IDictionary<Type, Func<object, object, bool>> _predicates = new Dictionary<Type, Func<object, object, bool>>();
private bool _withoutSort;
private int _maxRecursion = int.MaxValue;
private readonly HashSet<VisitedComparison> _visitedObjects = new HashSet<VisitedComparison>();
private static readonly HashSet<string> _globallyIgnoredNames = new HashSet<string>();
private static readonly HashSet<Type> _globallyIgnoredTypes = new HashSet<Type>();
private static readonly LinkedList<Type> _globallyIgnoredInterfaces = new LinkedList<Type>();
private static object _regionalTolerance;
public ContentsEqualConstraint(object expectedValue)
{
expected = expectedValue;
}
public ContentsEqualConstraint Comparing<T>(Func<T, T, bool> predicate)
{
Type t = typeof (T);
if (predicate == null)
{
_predicates.Remove(t);
}
else
{
_predicates[t] = (x, y) => predicate((T) x, (T) y);
}
return this;
}
public ContentsEqualConstraint Ignoring(string fieldName)
{
_ignoredNames.Add(fieldName);
return this;
}
public ContentsEqualConstraint Ignoring(Type fieldType)
{
if (fieldType.IsInterface)
{
_ignoredInterfaces.AddFirst(fieldType);
}
else
{
_ignoredTypes.Add(fieldType);
}
return this;
}
public ContentsEqualConstraint IgnoringSuffix(string suffix)
{
if (string.IsNullOrEmpty(suffix))
{
throw new ArgumentNullException("suffix");
}
_ignoredSuffixes.AddLast(suffix);
return this;
}
public ContentsEqualConstraint WithoutSort()
{
_withoutSort = true;
return this;
}
public ContentsEqualConstraint RecursingOnly(int levels)
{
_maxRecursion = levels;
return this;
}
public static void GlobalIgnore(string fieldName)
{
_globallyIgnoredNames.Add(fieldName);
}
public static void GlobalIgnore(Type fieldType)
{
if (fieldType.IsInterface)
{
_globallyIgnoredInterfaces.AddFirst(fieldType);
}
else
{
_globallyIgnoredTypes.Add(fieldType);
}
}
public static IDisposable RegionalIgnore(string fieldName)
{
return new RegionalIgnoreTracker(fieldName);
}
public static IDisposable RegionalIgnore(Type fieldType)
{
return new RegionalIgnoreTracker(fieldType);
}
public static IDisposable RegionalWithin(object tolerance)
{
return new RegionalWithinTracker(tolerance);
}
public override bool Matches(object actualValue)
{
typePathExpanded = null;
actual = actualValue;
return Matches(expected, actualValue);
}
private bool Matches(object expectedValue, object actualValue)
{
bool matches = true;
if (!MatchesNull(expectedValue, actualValue, ref matches))
{
return matches;
}
// DatesEqualConstraint supports tolerance in dates but works as equal constraint for everything else
Constraint eq = new DatesEqualConstraint(expectedValue).Within(tolerance ?? _regionalTolerance);
if (eq.Matches(actualValue))
{
return true;
}
if (MatchesVisited(expectedValue, actualValue, ref matches))
{
if (MatchesDictionary(expectedValue, actualValue, ref matches) &&
MatchesList(expectedValue, actualValue, ref matches) &&
MatchesType(expectedValue, actualValue, ref matches) &&
MatchesPredicate(expectedValue, actualValue, ref matches))
{
MatchesFields(expectedValue, actualValue, eq, ref matches);
}
}
return matches;
}
private bool MatchesNull(object expectedValue, object actualValue, ref bool matches)
{
if (IsNullEquivalent(expectedValue))
{
expectedValue = null;
}
if (IsNullEquivalent(actualValue))
{
actualValue = null;
}
if (expectedValue == null && actualValue == null)
{
matches = true;
return false;
}
if (expectedValue == null)
{
expectedDescription = "null";
actualDescription = "NOT null";
matches = Failure;
return false;
}
if (actualValue == null)
{
expectedDescription = "not null";
actualDescription = "null";
matches = Failure;
return false;
}
return true;
}
private bool MatchesType(object expectedValue, object actualValue, ref bool matches)
{
Type expectedType = expectedValue.GetType();
Type actualType = actualValue.GetType();
if (expectedType != actualType)
{
try
{
Convert.ChangeType(actualValue, expectedType);
}
catch(InvalidCastException)
{
expectedDescription = expectedType.FullName;
actualDescription = actualType.FullName;
matches = Failure;
return false;
}
}
return true;
}
private bool MatchesPredicate(object expectedValue, object actualValue, ref bool matches)
{
Type t = expectedValue.GetType();
Func<object, object, bool> predicate;
if (_predicates.TryGetValue(t, out predicate))
{
matches = predicate(expectedValue, actualValue);
return false;
}
return true;
}
private bool MatchesVisited(object expectedValue, object actualValue, ref bool matches)
{
var c = new VisitedComparison(expectedValue, actualValue);
if (_visitedObjects.Contains(c))
{
matches = true;
return false;
}
_visitedObjects.Add(c);
return true;
}
private bool MatchesDictionary(object expectedValue, object actualValue, ref bool matches)
{
if (expectedValue is IDictionary && actualValue is IDictionary)
{
var expectedDictionary = (IDictionary)expectedValue;
var actualDictionary = (IDictionary)actualValue;
if (expectedDictionary.Count != actualDictionary.Count)
{
expectedDescription = expectedDictionary.Count + " item dictionary";
actualDescription = actualDictionary.Count + " item dictionary";
matches = Failure;
return false;
}
foreach (DictionaryEntry expectedEntry in expectedDictionary)
{
if (!actualDictionary.Contains(expectedEntry.Key))
{
expectedDescription = expectedEntry.Key + " exists";
actualDescription = expectedEntry.Key + " does not exist";
matches = Failure;
return false;
}
if (CanRecurseFurther)
{
typePath.Push(expectedEntry.Key.ToString());
if (!Matches(expectedEntry.Value, actualDictionary[expectedEntry.Key]))
{
matches = Failure;
return false;
}
typePath.Pop();
}
}
matches = true;
return false;
}
return true;
}
private bool MatchesList(object expectedValue, object actualValue, ref bool matches)
{
if (!(expectedValue is IList && actualValue is IList))
{
return true;
}
var expectedList = (IList) expectedValue;
var actualList = (IList) actualValue;
if (!Matches(expectedList.Count, actualList.Count))
{
matches = false;
}
else
{
if (CanRecurseFurther)
{
int max = expectedList.Count;
if (max != 0 && !_withoutSort)
{
SafeSort(expectedList);
SafeSort(actualList);
}
for (int i = 0; i < max; i++)
{
typePath.Push(i.ToString());
if (!Matches(expectedList[i], actualList[i]))
{
matches = false;
return false;
}
typePath.Pop();
}
}
matches = true;
}
return false;
}
private void MatchesFields(object expectedValue, object actualValue, Constraint equalConstraint, ref bool matches)
{
Type expectedType = expectedValue.GetType();
FieldInfo[] fields = expectedType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
// should have passed the EqualConstraint check
if (expectedType.IsPrimitive ||
expectedType == typeof(string) ||
expectedType == typeof(Guid) ||
fields.Length == 0)
{
failedEquality = equalConstraint;
matches = Failure;
return;
}
if (expectedType == typeof(DateTime))
{
var expectedDate = (DateTime)expectedValue;
var actualDate = (DateTime)actualValue;
if (Math.Abs((expectedDate - actualDate).TotalSeconds) > 3.0)
{
failedEquality = equalConstraint;
matches = Failure;
return;
}
matches = true;
return;
}
if (CanRecurseFurther)
{
while(true)
{
foreach (FieldInfo field in fields)
{
if (!Ignore(field))
{
typePath.Push(field.Name);
if (!Matches(GetValue(field, expectedValue), GetValue(field, actualValue)))
{
matches = Failure;
return;
}
typePath.Pop();
}
}
expectedType = expectedType.BaseType;
if (expectedType == null)
{
break;
}
fields = expectedType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
}
}
matches = true;
return;
}
private bool Ignore(FieldInfo field)
{
if (_ignoredNames.Contains(field.Name) ||
_ignoredTypes.Contains(field.FieldType) ||
_globallyIgnoredNames.Contains(field.Name) ||
_globallyIgnoredTypes.Contains(field.FieldType) ||
field.GetCustomAttributes(typeof (IgnoreContentsAttribute), false).Length != 0)
{
return true;
}
foreach(string ignoreSuffix in _ignoredSuffixes)
{
if (field.Name.EndsWith(ignoreSuffix))
{
return true;
}
}
foreach (Type ignoredInterface in _ignoredInterfaces)
{
if (ignoredInterface.IsAssignableFrom(field.FieldType))
{
return true;
}
}
return false;
}
private static bool Failure
{
get
{
return false;
}
}
private static bool IsNullEquivalent(object value)
{
return value == null ||
value == DBNull.Value ||
(value is int && (int) value == int.MinValue) ||
(value is double && (double) value == double.MinValue) ||
(value is DateTime && (DateTime) value == DateTime.MinValue) ||
(value is Guid && (Guid) value == Guid.Empty) ||
(value is IList && ((IList)value).Count == 0);
}
private static object GetValue(FieldInfo field, object source)
{
try
{
return field.GetValue(source);
}
catch(Exception ex)
{
return ex;
}
}
public override void WriteMessageTo(MessageWriter writer)
{
if (TypePath.Length != 0)
{
writer.WriteLine("Failure on " + TypePath);
}
if (failedEquality != null)
{
failedEquality.WriteMessageTo(writer);
}
else
{
base.WriteMessageTo(writer);
}
}
public override void WriteDescriptionTo(MessageWriter writer)
{
writer.Write(expectedDescription);
}
public override void WriteActualValueTo(MessageWriter writer)
{
writer.Write(actualDescription);
}
private string TypePath
{
get
{
if (typePathExpanded == null)
{
string[] p = typePath.ToArray();
Array.Reverse(p);
var text = new StringBuilder(128);
bool isFirst = true;
foreach(string part in p)
{
if (isFirst)
{
text.Append(part);
isFirst = false;
}
else
{
int i;
if (int.TryParse(part, out i))
{
text.Append("[" + part + "]");
}
else
{
text.Append("." + part);
}
}
}
typePathExpanded = text.ToString();
}
return typePathExpanded;
}
}
private bool CanRecurseFurther
{
get
{
return typePath.Count < _maxRecursion;
}
}
private static bool SafeSort(IList list)
{
if (list == null)
{
return false;
}
if (list.Count < 2)
{
return true;
}
try
{
object first = FirstNonNull(list) as IComparable;
if (first == null)
{
return false;
}
if (list is Array)
{
Array.Sort((Array)list);
return true;
}
return CallIfExists(list, "Sort");
}
catch
{
return false;
}
}
private static object FirstNonNull(IEnumerable enumerable)
{
if (enumerable == null)
{
throw new ArgumentNullException("enumerable");
}
foreach (object item in enumerable)
{
if (item != null)
{
return item;
}
}
return null;
}
private static bool CallIfExists(object instance, string method)
{
if (instance == null)
{
throw new ArgumentNullException("instance");
}
if (String.IsNullOrEmpty(method))
{
throw new ArgumentNullException("method");
}
Type target = instance.GetType();
MethodInfo m = target.GetMethod(method, new Type[0]);
if (m != null)
{
m.Invoke(instance, null);
return true;
}
return false;
}
#region VisitedComparison Helper
private class VisitedComparison
{
private readonly object _expected;
private readonly object _actual;
public VisitedComparison(object expected, object actual)
{
_expected = expected;
_actual = actual;
}
public override int GetHashCode()
{
return GetHashCode(_expected) ^ GetHashCode(_actual);
}
private static int GetHashCode(object o)
{
if (o == null)
{
return 0;
}
return o.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != typeof(VisitedComparison))
{
return false;
}
var other = (VisitedComparison) obj;
return _expected == other._expected &&
_actual == other._actual;
}
}
#endregion
#region RegionalIgnoreTracker Helper
private class RegionalIgnoreTracker : IDisposable
{
private readonly string _fieldName;
private readonly Type _fieldType;
public RegionalIgnoreTracker(string fieldName)
{
if (!_globallyIgnoredNames.Add(fieldName))
{
_globallyIgnoredNames.Add(fieldName);
_fieldName = fieldName;
}
}
public RegionalIgnoreTracker(Type fieldType)
{
if (!_globallyIgnoredTypes.Add(fieldType))
{
_globallyIgnoredTypes.Add(fieldType);
_fieldType = fieldType;
}
}
public void Dispose()
{
if (_fieldName != null)
{
_globallyIgnoredNames.Remove(_fieldName);
}
if (_fieldType != null)
{
_globallyIgnoredTypes.Remove(_fieldType);
}
}
}
#endregion
#region RegionalWithinTracker Helper
private class RegionalWithinTracker : IDisposable
{
public RegionalWithinTracker(object tolerance)
{
_regionalTolerance = tolerance;
}
public void Dispose()
{
_regionalTolerance = null;
}
}
#endregion
#region IgnoreContentsAttribute
[AttributeUsage(AttributeTargets.Field)]
public sealed class IgnoreContentsAttribute : Attribute
{
}
#endregion
}
public class DatesEqualConstraint : EqualConstraint
{
private readonly object _expected;
public DatesEqualConstraint(object expectedValue) : base(expectedValue)
{
_expected = expectedValue;
}
public override bool Matches(object actualValue)
{
if (tolerance != null && tolerance is TimeSpan)
{
if (_expected is DateTime && actualValue is DateTime)
{
var expectedDate = (DateTime) _expected;
var actualDate = (DateTime) actualValue;
var toleranceSpan = (TimeSpan) tolerance;
if ((actualDate - expectedDate).Duration() <= toleranceSpan)
{
return true;
}
}
tolerance = null;
}
return base.Matches(actualValue);
}
}
}
This is actually a simple process. Using reflection you can compare each field on the object.
public static Boolean ObjectMatches(Object x, Object y)
{
if (x == null && y == null)
return true;
else if ((x == null && y != null) || (x != null && y == null))
return false;
Type tx = x.GetType();
Type ty = y.GetType();
if (tx != ty)
return false;
foreach(FieldInfo field in tx.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
if (field.FieldType.IsValueType && (field.GetValue(x).ToString() != field.GetValue(y).ToString()))
return false;
else if (field.FieldType.IsClass && !ObjectMatches(field.GetValue(x), field.GetValue(y)))
return false;
}
return true;
}
Using the Nuget suggested by Jesse and this code I managed to compare two objects with great results.
using KellermanSoftware.CompareNetObjects;
using System;
namespace MyProgram.UnitTestHelper
{
public class ObjectComparer
{
public static bool ObjectsHaveSameValues(object first, object second)
{
CompareLogic cl = new CompareLogic();
ComparisonResult result = cl.Compare(first, second);
if (!result.AreEqual)
Console.WriteLine(result.DifferencesString);
return result.AreEqual;
}
}
}
Here is a simple comparer that we've used with unit testing to assert that two objects have equal properties. It's a mash-up of ideas found in various articles, and handles circular references.
public static class TestHelper
{
public static void AssertAreEqual(Object expected, Object actual, String name)
{
// Start a new check with an empty list of actual objects checked
// The list of actual objects checked is used to ensure that circular references don't result in infinite recursion
List<Object> actualObjectsChecked = new List<Object>();
AssertAreEqual(expected, actual, name, actualObjectsChecked);
}
private static void AssertAreEqual(Object expected, Object actual, String name, List<Object> actualObjectsChecked)
{
// Just return if already checked the actual object
if (actualObjectsChecked.Contains(actual))
{
return;
}
actualObjectsChecked.Add(actual);
// If both expected and actual are null, they are considered equal
if (expected == null && actual == null)
{
return;
}
if (expected == null && actual != null)
{
Assert.Fail(String.Format("The actual value of {0} was not null when null was expected.", name));
}
if (expected != null && actual == null)
{
Assert.Fail(String.Format("The actual value of {0} was null when an instance was expected.", name));
}
// Get / check type info
// Note: GetType always returns instantiated (i.e. most derived) type
Type expectedType = expected.GetType();
Type actualType = actual.GetType();
if (expectedType != actualType)
{
Assert.Fail(String.Format("The actual type of {0} was not the same as the expected type.", name));
}
// If expected is a Primitive, Value, or IEquatable type, assume Equals is sufficient to check
// Note: Every IEquatable type should have also overridden Object.Equals
if (expectedType.IsPrimitive || expectedType.IsValueType || expectedType.IsIEquatable())
{
Assert.IsTrue(expected.Equals(actual), "The actual {0} is not equal to the expected.", name);
return;
}
// If expected is an IEnumerable type, assume comparing enumerated items is sufficient to check
IEnumerable<Object> expectedEnumerable = expected as IEnumerable<Object>;
IEnumerable<Object> actualEnumerable = actual as IEnumerable<Object>;
if ((expectedEnumerable != null) && (actualEnumerable != null))
{
Int32 actualEnumerableCount = actualEnumerable.Count();
Int32 expectedEnumerableCount = expectedEnumerable.Count();
// Check size first
if (actualEnumerableCount != expectedEnumerableCount)
{
Assert.Fail(String.Format("The actual number of enumerable items in {0} did not match the expected number.", name));
}
// Check items in order, assuming order is the same
for (int i = 0; i < actualEnumerableCount; ++i)
{
AssertAreEqual(expectedEnumerable.ElementAt(i), actualEnumerable.ElementAt(i), String.Format("{0}[{1}]", name, i), actualObjectsChecked);
}
return;
}
// If expected is not a Primitive, Value, IEquatable, or Ienumerable type, assume comparing properties is sufficient to check
// Iterate through properties
foreach (PropertyInfo propertyInfo in actualType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
// Skip properties that can't be read or require parameters
if ((!propertyInfo.CanRead) || (propertyInfo.GetIndexParameters().Length != 0))
{
continue;
}
// Get properties from both
Object actualProperty = propertyInfo.GetValue(actual, null);
Object expectedProperty = propertyInfo.GetValue(expected, null);
AssertAreEqual(expectedProperty, actualProperty, String.Format("{0}.{1}", name, propertyInfo.Name), actualObjectsChecked);
}
}
public static Boolean IsIEquatable(this Type type)
{
return type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEquatable<>));
}
}

PropertyInfo.GetValue() - how do you index into a generic parameter using reflection in C#?

This (shortened) code..
for (int i = 0; i < count; i++)
{
object obj = propertyInfo.GetValue(Tcurrent, new object[] { i });
}
.. is throwing a 'TargetParameterCountException : Parameter count mismatch' exception.
The underlying type of 'propertyInfo' is a Collection of some T. 'count' is the number of items in the collection. I need to iterate through the collection and perform an operation on obj.
Advice appreciated.
Reflection only works on one level at a time.
You're trying to index into the property, that's wrong.
Instead, read the value of the property, and the object you get back, that's the object you need to index into.
Here's an example:
using System;
using System.Collections.Generic;
using System.Reflection;
namespace DemoApp
{
public class TestClass
{
public List<Int32> Values { get; private set; }
public TestClass()
{
Values = new List<Int32>();
Values.Add(10);
}
}
class Program
{
static void Main()
{
TestClass tc = new TestClass();
PropertyInfo pi1 = tc.GetType().GetProperty("Values");
Object collection = pi1.GetValue(tc, null);
// note that there's no checking here that the object really
// is a collection and thus really has the attribute
String indexerName = ((DefaultMemberAttribute)collection.GetType()
.GetCustomAttributes(typeof(DefaultMemberAttribute),
true)[0]).MemberName;
PropertyInfo pi2 = collection.GetType().GetProperty(indexerName);
Object value = pi2.GetValue(collection, new Object[] { 0 });
Console.Out.WriteLine("tc.Values[0]: " + value);
Console.In.ReadLine();
}
}
}
I was most of the way there until I saw this, and I am posting this because I didn't see it anywhere else; the key was using GetValue(collection, new Object[] { i }); in the loop rather than trying to use GetValue(collection, new Object[i]); outside the loop.
(You can probably ignore the "output" in my example);
private static string Recursive(object o)
{
string output="";
Type t = o.GetType();
if (t.GetProperty("Item") != null)
{
System.Reflection.PropertyInfo p = t.GetProperty("Item");
int count = -1;
if (t.GetProperty("Count") != null &&
t.GetProperty("Count").PropertyType == typeof(System.Int32))
{
count = (int)t.GetProperty("Count").GetValue(o, null);
}
if (count > 0)
{
object[] index = new object[count];
for (int i = 0; i < count; i++)
{
object val = p.GetValue(o, new object[] { i });
output += RecursiveWorker(val, p, t);
}
}
}
return output;
}
Assembly zip_assembly = Assembly.LoadFrom(#"C:\Ionic.Zip.Reduced.dll");
Type ZipFileType = zip_assembly.GetType("Ionic.Zip.ZipFile");
Type ZipEntryType = zip_assembly.GetType("Ionic.Zip.ZipEntry");
string local_zip_file = #"C:\zipfile.zip";
object zip_file = ZipFileType.GetMethod("Read", new Type[] { typeof(string) }).Invoke(null, new object[] { local_zip_file });
// Entries is ICollection<ZipEntry>
IEnumerable entries = (IEnumerable)ZipFileType.GetProperty("Entries").GetValue(zip_file, null);
foreach (object entry in entries)
{
string file_name = (string)ZipEntryType.GetProperty("FileName").GetValue(entry, null);
Console.WriteLine(file_name);
}

Categories