dynamically work with different classes - c#

I have a class:
public class Test1
{
public void assignData(List<CustomClass> customData, string targetFieldName)
{
for(int i=0; i<customData.Count; i++)
{
if(customData[i].targetFieldName)
{
customData[i].targetFieldName = newValue;
}
}
}
}
List<customClass1> list1;
List<customClass2> list2;
customClass1 and customClass2 are completely different, but they share the same field 'dateAdded'. I want to be able to call Test1.assignData(list1, "dateAdded") and Test1.assignData(list2, "dateAdded"). and the list1 and list2 will get updated. How can I do that? Thanks!

The best way to do this is to have a common interface that they both implement which exposes the dateAdded field as a property
interface ICustomClass {
DateTime dateAdded { get; set; }
}
Then both classes can implement that interface and you can change the function to use that interface
public void assignData(IEnumerable<ICustomClass> enumerable) {
foreach (var customData in enumerable) {
customData.dateAdded = newValue;
}
}
EDIT
In the comments the OP stated their desire to make this update to any list irrespective of the interface. In that case the likely best course is to use dynamic
public void assignData(IEnumerable<object> enumerable) {
foreach (dynamic customData in enumerable) {
try {
customData.dateAdded = newValue;
} catch {
// Object doesn't have dateAdded so just move on
}
}
}

If CustomClass1 and CustomClass2both deriving from CustomClass and you want to simply set value of targetFieldName , all you need to do is replace List<T> with IEnumerable<T>.
Make sure the common field is in base class so that it can be accessed without worrying about the derived implementation.
public void assignData(List<CustomClass> customData, string targetFieldName)
with
public void assignData(IEnumerable<CustomClass> customData,
string targetFieldName)
With this you can call it for both lists because of covariance. Simple example -
IEnumerable<object> list = new List<string>(); // This will work
List<object> list = new List<string>(); // This won't compile.

So I totally agree with #JaredPar that this sounds like you need a common interface but it is possible with dynamics.
Note that this example code doesn't behave properly if DateAdded isn't present
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace dynamics_test
{
class CustomOne
{
public string NotInCustomTwo { get; set; }
public DateTime DateAdded { get; set; }
}
class CustomTwo
{
public string NotInCustomOne { get; set; }
public DateTime DateAdded { get; set; }
}
[TestFixture]
public class TestDynamics
{
private List<CustomOne> _customOnes;
private List<CustomTwo> _customTwos;
[SetUp]
public void Setup()
{
this._customOnes = new List<CustomOne>()
{
new CustomOne {DateAdded = DateTime.Now.AddDays(1), NotInCustomTwo = "some value"},
new CustomOne {DateAdded = DateTime.Now, NotInCustomTwo = "some value"}
};
this._customTwos = new List<CustomTwo>()
{
new CustomTwo {DateAdded = DateTime.Now.AddDays(1), NotInCustomOne = "some value"},
new CustomTwo {DateAdded = DateTime.Now, NotInCustomOne = "some value"}
};
}
[Test]
public void DynamicsAllowBadThingsMkay()
{
var dynamics = _customOnes.Cast<dynamic>().ToList();
dynamics.AddRange(_customTwos);
Assert.AreEqual(2, dynamics.Count(d=>d.DateAdded.Date == DateTime.Now.Date));
foreach (var thing in dynamics)
{
Console.WriteLine(thing.DateAdded);
}
}
}
}

Related

C# foreach loop thru collection of unknown type

I have a generic method that can be called with 2 different object types, TypeA or TypeB. TypeA and TypeB are essentially identical classes except in name only. I am trying to determine how to prevent from having to duplicate the Foreach loop code for each object type. Is this possible ? thanks.
public class TypeA
{
public string Name { get; set; }
public string Department { get; set; }
public string Total { get; set; }
}
public class TypeB
{
public string Name { get; set; }
public string Department { get; set; }
public string Total { get; set; }
}
private CsvExport GenerateExport<T>(IEnumerable<T> scores)
{
CsvExport export = new CsvExport();
List<TypeA> aList = null;
List<TypeB> bList = null;
Type type = scores.GetType();
if (type.FullName.Contains("TypeA"))
{
aList = scores as List<ObjectaModel>;
}
else if (type.FullName.Contains("TypeB"))
{
bList = scores as List<ObjectbModel>;
}
foreach (var dt in aList)
{
export.AddRow();
export["Name"] = dt.Name;
export["Department"] = dt.Department;
export["Total "] = dt.Total;
};
return export;
}
In this particular case I strongly suggest you delegate the hard work to the CsvHelper library which you can also obtain from Nuget and is used like this...
public void ExportToCsv<T>(string filename, ImmutableArray<T> objects)
{
using (var writer = File.CreateText(filename))
{
var csv = new CsvWriter(writer);
csv.WriteRecords(objects);
}
}
The more general answer to your question is that you must either have both classes inherit from a common class or interface or you would have to use reflection to look for an obtain the values of the named properties.
Using a common interface...
public interface IScore
{
int HiScore {get;}
}
public class ScrabbleScore : IScore
{
public int HiScore {get;set;}
}
public class PacManScore : IScore
{
public int HiScore {get;set;}
}
public void Export<T>(IEnumerable<T> scores) where T: IScore
{
foreach(var s in scores)
{
CvsExport["Hi score"]= s.HiScore;
}
}
Using reflection...
var CsvExport = new Dictionary<string,string>();
foreach(var o in scores)
{
//note that checking the type for each object enables you to have heterogenous lists if you want
var objectType= o.GetType();
foreach(var p in objectType.GetProperties())
{
var propertyName = p.Name;
CsvExport[propertyName] = p.GetValue(o).ToString();
}
}
I would treat the reflection solution as the least favoured of the three.

Cast generic collection item type

The following code illustrates a situation I'm having. The real code use different names and get values in other ways, but they match with what I need an answer. Specifically in lines 76-89 (the only lines I control) I need to extract a variable of type "ICollection" with the values and I don't like none of the used approaches. Is there another approach to do it without to create class "AbstractCollection"?
namespace ConsoleApp1
{
using System.Collections.Generic;
using System.Linq;
interface IEntity
{
string Id { get; }
string Name { get; }
}
class Entity : IEntity
{
public Entity(string id, string name)
{
Id = id;
Name = name;
}
public string Id { get; }
public string Name { get; }
}
interface ICollection<TGeneric>
{
IEnumerable<TGeneric> Items { get; }
}
class Collection<TGeneric> : ICollection<TGeneric> where TGeneric : Entity, IEntity
{
public IEnumerable<TGeneric> Items { get; set; }
}
class AbstractCollection<TConcrete, TAbstract> : ICollection<TAbstract> where TAbstract : class, IEntity
{
public AbstractCollection(ICollection<TConcrete> collection)
{
this._Items = new List<TAbstract>();
if (collection?.Items != null)
{
foreach (TConcrete concreteItem in collection.Items)
{
TAbstract abstractItem = concreteItem as TAbstract;
this._Items.Add(abstractItem);
}
}
}
public IEnumerable<TAbstract> Items
{
get { return this._Items; }
set { this._Items = value?.ToList(); }
}
private IList<TAbstract> _Items { get; set; }
}
class EntityCollection : Collection<Entity>
{
public EntityCollection()
{
var items = new List<Entity>()
{
new Entity("1", "Name1"),
new Entity("2", "Name2"),
new Entity("3", "Name3")
};
Items = items;
}
}
class Context
{
public Context()
{
var concreteItems = new EntityCollection();
// I can modify from this line to the end of the method but not any code before.
// I expected values in "list1" but is null.
var list1 = concreteItems as ICollection<IEntity>;
var list2 = concreteItems as ICollection<Entity>;
var abstractItems = new List<IEntity>();
foreach (Entity concreteItem in concreteItems.Items)
{
IEntity abstractItem = concreteItem as IEntity;
abstractItems.Add(abstractItem);
}
// Why "list3" is null?
var list3 = abstractItems as ICollection<IEntity>;
// I want to avoid class "AbstractCollection"
var list4 = new AbstractCollection<Entity, IEntity>(list2);
// Finally "list5" has value in the way I want it.
var list5 = list4 as ICollection<IEntity>;
}
}
class Program
{
static void Main(string[] args)
{
var context = new Context();
}
}
}
Covariance guides to the solution:
interface ICollection<out TGeneric>
{
IEnumerable<TGeneric> Items { get; }
}

C# a having a list inside a list of objects. Will not allow .add

I keep getting this error.
An unhandled exception of type 'System.NullReferenceException' occurred in CustomerExcelreader.exe
Additional information:
Object reference not set to an instance of an object.
Which I don't know why, since I have a global object, and I know the object works, since when I use non-list values of the object, everything works just fine.
This is the actual object in question.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CustomerExcelreader
{
public class customers
{
public customers()
{
}
/// <summary>
/// These work just fine.
/// </summary>
public String Guest { get; set; }
public String Contact { get; set; }
public String ClubNumber { get; set; }//GuestNumber
public String CasioNumber { get; set; }
/// <summary>
/// Lists of lists
/// </summary>
public List<String> Dates { get; set; }
public List<String> Description { get; set; }
public List<Double> moneyIN { get; set; }
public List<Double> moneyOUT { get; set; }
public List<Double> Balance { get; set; }
public List<String> Remarks { get; set; }
}
}
and here is where it messes up with any of the List values, but normal variables are fine.
private void addButton_Click(object sender, EventArgs e)
{
//customers add_customerHistory;
if (listNames.SelectedIndex >= 0)// && listNames.SelectedIndex <= Pathlist.Capacity)//somehow SelectedIndex could be less then 0 by not picking something.
{
//add_customerHistory = CustomerHistoryList[listNames.SelectedIndex];
textBox1.Text = CustomerHistoryList[listNames.SelectedIndex].ClubNumber;
CustomerHistoryList[listNames.SelectedIndex].Dates.Add(DateBox.Text);
CustomerHistoryList[listNames.SelectedIndex].Description.Add(DescriptionBox.Text);
CustomerHistoryList[listNames.SelectedIndex].moneyIN.Add(Convert.ToDouble(InBox.Text));
CustomerHistoryList[listNames.SelectedIndex].moneyOUT.Add(Convert.ToDouble(OutBox.Text));
CustomerHistoryList[listNames.SelectedIndex].Balance.Add(Convert.ToDouble(BalanceBox.Text));
CustomerHistoryList[listNames.SelectedIndex].Remarks.Add(RemarkBox.Text);
//CustomerHistoryList[listNames.SelectedIndex].CasioNumber = "Its been changed";
richTextBox1.Text = CustomerHistoryList[listNames.SelectedIndex].CasioNumber;
}
else
{
CustomerHistoryList[0].Dates.Add(DateBox.Text);
CustomerHistoryList[0].Description.Add(DescriptionBox.Text);
CustomerHistoryList[0].moneyIN.Add(Convert.ToDouble(InBox.Text));
CustomerHistoryList[0].moneyOUT.Add(Convert.ToDouble(OutBox.Text));
CustomerHistoryList[0].Balance.Add(Convert.ToDouble(BalanceBox.Text));
CustomerHistoryList[0].Remarks.Add(RemarkBox.Text);
}
}
It seems odd that it doesn't work here. I guess it has something to do with lists inside of objects? I heard those can act funky.
You need to initialise your list<String> properties in your class constuctor.
public class customers
{
public customers()
{
Dates = new List<String>();
Description = new List<String>();
moneyIN = new List<Double>();
moneyOUT = new List<Double>();
Balance = new List<Double>();
Remarks = new List<String>();
}
// Other bits (not copied)
}
You have to create a new instance of a class (List in your case), before accesing its members (Add method in your case).
For example:
CustomerHistoryList[listNames.SelectedIndex].Dates = new List<string>();
CustomerHistoryList[listNames.SelectedIndex].Dates.Add(DateBox.Text);
As Chris Hammond pointed out, just because you use a property setter/getter does not mean that it will automatically create property values for you. This only happens with non nullable value types.
Either put the logic in your constructor or change them to private fields.
public class customers
{
public customers()
{
this.Dates = new List<String>();
this.Description = new List<String>();
...
}
This makes it tricky for inheritance, so you can utilize private fields
public class customers
{
private List<String> _dates = new List<String>();
private List<String> _description = new List<String>();
public List<String> Dates
{
{ return _dates; }
{ _dates = value; }
}
public List<String> Description
{
{ return _description; }
{ _description = value; }
}
....
See this other post about exposing Generic Lists as Properties
Why is it considered bad to expose List<T>?

C# simple reflection

I have a class that contains another poco class with simple get set properties:
public class PersonalInformation {
public string FirstName { get; set; }
public string FirstSomethingElse { get; set; }
}
I would like to find out if the current instance's PersonalInformation.FirstName has a value. I can't figure out how to obtain it via reflection:
foreach (PropertyInfo property in this.PersonalInformation.GetType().GetProperties())
{
if (property.Name.Contains("First"))
{
if (property.GetValue(XXX, null) != null)
do something...
}
}
The instance I have is "this", which does not work, neither does this.PersonalInformation. What am I doing wrong?
Thank you for your response,
Aldo
Addendum: I'm using ASP.NET MVC3. In my razor view I can do the following very easily:
foreach (var property in Model.PersonalInformation.GetType().GetProperties())
{
<div class="editor-line">
#if (property.Name != null)
{
<label>#(property.Name)</label>
#Html.Editor(property.Name)
}
</div>
}
there is a property.Value member that returns the current value of the field. This field comes from a poco class, as you see above. What would be the equivalent code in the code-behind?
this.PersonalInformation certainly should work. After all, that's the target you're talking about.
Sample code:
using System;
using System.Reflection;
public class PersonalInformation {
public string FirstName { get; set; }
public string FirstSomethingElse { get; set; }
}
public class Foo
{
public PersonalInformation PersonalInformation { get; set; }
public void ShowProperties()
{
foreach (var property in this.PersonalInformation
.GetType()
.GetProperties())
{
var value = property.GetValue(this.PersonalInformation, null);
Console.WriteLine("{0}: {1}", property.Name, value);
}
}
}
class Test
{
static void Main()
{
Foo foo = new Foo {
PersonalInformation = new PersonalInformation {
FirstName = "Fred",
FirstSomethingElse = "XYZ"
}
};
foo.ShowProperties();
}
}
Although if you just "want to find out if the current instance's PersonalInformation.FirstName has a value" then I don't see why you're using reflection...
GetProperties returns a PropertyInfo[], not a single PropertyInfo.

List a classes generic interface names

I have this c# code;
case "Cafe":
source.trendItem = new TrendingLocation<ITrendingCafe>();
break;
case "Pub":
source.trendItem = new TrendingLocation<ITrendingPub>();
break;
etc
a trendItem is defined like this;
public class TrendingItem<T> where T : ITrendingItem
{
public T trendItem { get; set; }
}
Then I have this;
public List<TrendingItem<ITrendingItem>> trendItems { get; set; }
Now for each item in the above trendItems i want to get the interfaces.
I tried using;
string g = fvm.trendItems[4].trendItem.GetType().GetInterfaces()[1].Name;
and
string g = typeof(TrendingLocation<>).GetInterfaces()[0].Name;
but neither of these lists the Generic interface such as ITrendingCafe, ITrendingRestaurant etc.
Is there a way I can get the name of the generic interface name?
You want to use the Type's GetGenericArguments method.
If I understand your structure, it will be something like:
Type[] typeArguments = fvm.trendItems[4].trendItem.GetType().GetGenericArguments();
foreach (Type tParam in typeArguments)
{
// Compare the type with the interface you are looking for.
}
I take it that ITrendingCafe is an interface that implements ITrendingItem. I wrote a quick program that takes and displays all of the interfaces that T Implements:
using System;
using System.Collections.Generic;
namespace TestConsoleApplication
{
public interface ITrendingItem
{
string ItemName { get; set; }
}
public interface ITrendingCafe : ITrendingItem
{
string CafeName { get; set; }
}
public class TrendingItem<T> where T : ITrendingItem
{
public T trendItem { get; set; }
}
public class Cafe : ITrendingCafe
{
public string ItemName { get; set; }
public string CafeName { get; set; }
}
class Program
{
static void Main(string[] args)
{
var test = new List<TrendingItem<ITrendingItem>> { new TrendingItem<ITrendingItem> { trendItem = new Cafe() } };
foreach (var trendingItem in test[0].trendItem.GetType().GetInterfaces())
{
Console.Out.WriteLine(trendingItem.Name);
}
Console.ReadKey();
}
}
}
And here is the output:
As you can see, the interface is there. Just loop through and find the one you need!

Categories