Following up on a previous question, here is my general class structure..
Combine Elements in a List based on Type and Summate their Values, LINQ
I am trying to combine the items using their respective .Add methods, but because of Entity Framework considerations, I can only deal with base types (Not interfaces).
using System.Linq;
using System.Collections.Generic;
interface Item<T>
{
T Add(T item);
}
abstract class Item
{
public ItemType Type;
public int Value;
}
class SubItemOne : Item, Item<SubItemOne>
{
public int Additional { get; set; }
public SubItemOne Add(SubItemOne item)
{
// ..
}
}
class SubItemTwo : Item, Item<SubItemTwo>
{
public int MoreData { get; set; }
public int OtherData { get; set; }
public SubItemTwo Add(SubItemTwo item)
{
// ...
}
}
class SubItemThree : Item, Item<SubItemThree>
{
public int StillOtherData { get; set; }
public int SecondaryData { get; set; }
public int TertiaryData { get; set; }
public SubItemThree Add(SubItemThree item)
{
// ...
}
}
class ItemType
{
public string Name;
}
class Test
{
public static void Main()
{
List<ItemType> types = new List<ItemType>();
types.Add(new ItemType { Name = "Type1" });
types.Add(new ItemType { Name = "Type2" });
types.Add(new ItemType { Name = "Type3" });
List<Item> items = new List<Item>();
for (int i = 0; i < 10; i++)
{
items.Add(new SubItemOne
{
Type = types.Single(t => t.Name == "Type1"),
Additional = i
});
}
for (int i = 0; i < 10; i++)
{
items.Add(new SubItemTwo
{
Type = types.Single(t => t.Name == "Type2"),
MoreData = 10,
OtherData = i + 10
});
}
for (int i = 0; i < 10; i++)
{
items.Add(new SubItemThree
{
Type = types.Single(t => t.Name == "Type3"),
SecondaryData = 1000,
StillOtherData = 1874,
TertiaryData = i * 10
});
}
List<Item> combined = new List<Item>();
// create a list with 3 items, one of each 'type', with the sum of the total values of that type.
// types included are not always known at runtime.
// I am trying to invoke the .Add Method on the items so that they can be collatted together. However
// they all return as the base type.
}
}
The problem is that you're going to have to deal with the interfaces at a higher level, and thus won't be able to do so generically (i.e., using the generic type arguments). You're going to have to define a non-generic interface (even if it's in addition to your current generic interface) that these classes implement in order to do what you're after. Item<SubItemOne> is no more related to Item<SubItemTwo> in terms of assignment compatibility than string is to DateTime.
If you must do it this way, then you'll have to use reflection. This should do the trick:
Dictionary<ItemType, Item> sum = new Dictionary<ItemType, Item>();
foreach (var item in items)
{
Item currSum;
if (sum.TryGetValue(item.Type, out currSum))
{
sum[item.Type] = (Item)item.GetType().GetInterfaces().Single(
i => i.Name == "Item").GetMethod("Add")
.Invoke(currSum, new object[] { item });
}
else
{
sum.Add(item.Type, item);
}
}
List<Item> combined = sum.Values.ToList();
To detail on how the problem was solved. I would like to thank both people who assisted with this answer, (also I want to thank jarret, from my previous question involving the linq query) for helping me to get the resolution entirely.
I have found both solutions to run substantially well, though I do not like the Microsoft.CSharp.dll library staring at me in The Resources folder (I know you're there, even if I collapse you! Don't try to deny it!)
In all, LINQ continues to amaze me, but I was shocked and astounded that dynamic worked more than anything. To test, I threw a "NotImplementedException" in each of the three derivitive classes "Add" methods and ran the entire execution, when the compiler broke out to show the Exception, it was in the appropriate class. This tested true in both cases.
Additionally, this showed that parameters are in fact different from names when it comes to implementation initialization where reflection is concerned. I tested the same for creating an interface IItem and IItem<T> and got the same results. (Additionally, IItem`1 also yielded the same results when IItem<T> was the implemented interface).
Each time I get an answer from Stackoverflow, it continues to amaze and astound me that no matter how much I think I know, there are so many people that know more, but not only that, that they are browsing the same resource sites that I am, and willing to answer such piddly questions that eat up their time and resources. Thanks again to all of you who helped!
Using Adam Robinson's Method, I approached it from this angle.
var grouped = items.GroupBy(i => i.Name);
grouped.ForEach(x => {
x.ForEach(e => {
// results handled here
// assumes that all objects inheriting "Item" implement IItem, which contracts "Add" method.
e.GetType().GetInterfaces().First(i => i.Name == "IItem").GetMethod("Add").Invoke(e, new object[] { e });
});
});
Using arootbeer's answer.
var grouped = items.GroupBy(i => i.Name);
grouped.ForEach(x => {
x.ForEach(e => {
// results handled here
((dynamic)e).Add((dynamic)e);
});
});
Related
Consider this code.
distinctBrandIds is a collection of type long
List<long> distinctBrandIds = new List<long>{ 1, 5, 4};
BrandYesNo is a Dictionary with BrandId as key
Dictionary<long, bool> BrandYesNo = new Dictionary<long, bool>();
BrandYesNo.Add(1, false);
BrandYesNo.Add(2, false);
BrandYesNo.Add(3, false);
SomeClass has BrandId property
public class SomeClass{
public int ItemId {get; set;}
public long BrandId {get; set;}
// other properties
}
Items from CollectionOfSomeClass are removed when BrandId equals BrandId key in BrandYesNo dictionary with the value of false
distinctBrandIds.ForEach((v) =>
{
if (!(BrandYesNo[v]))
{
CollectionOfSomeClass.RemoveAll(sc => sc.BrandId == v);
}
});
In other places, the same code repeats with other collections of types in place of CollectionOfSomeClass.
The common thing is that the type of which the other collection is made of also has BrandId. So the check is always on BrandId property.
To create a generic method there are suggestions to use reflection and on those lines I have this:
public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass, List<long> distinctBrandIds, object propertyToCheck) where T : class
{
distinctBrandIds.ForEach((v) =>
{
if (!(BrandYesNo[v]))
{
CollectionOfSomeClass.RemoveAll((rd) => {
PropertyInfo pi = typeof(T).GetProperty("BrandId");
pi.GetValue(rd) == v;
});
}
});
}
The predicate is not correct.
How do I proceed with this?
Thanks in advance.
Accepted Answer
Initially that was not the case but I convinced the powers that be for CodeCaster's solution.
No need for reflection here, if you know and control the types that are to be processed by this method. Given you say
The common thing is that the type of which the other collection is made of also has BrandId. So the check is always on BrandId property.
Create an interface:
public interface IBrand
{
long BrandId { get; }
}
Apply that to the appropriate classes:
public class SomeClass : IBrand
{ ... }
And modify your constraint:
public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass, List<long> distinctBrandIds)
where T : IBrand
{
distinctBrandIds.ForEach((v) =>
{
if (!(BrandYesNo[v]))
{
CollectionOfSomeClass.RemoveAll(rd => rd.BrandId == v);
}
}
}
To illustrate my comment, the following code should work but will be slow:
public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass,
List<long> distinctBrandIds)
where T : class
{
PropertyInfo pi = typeof(T).GetProperty("BrandId");
distinctBrandIds.ForEach(v =>
{
if (!(BrandYesNo[v]))
{
CollectionOfSomeClass.RemoveAll(rd => ((long)pi.GetValue(rd) == v));
}
});
}
Or use Equals.
Note that this code should only be used if you don't have access to the classes code or cannot modify it. If not the case, #CodeMaster's solution is much safer + faster.
Similarly to #vc 74s answer, you could use reflection but cache it using a dictionary:
// Stores functions that take objects with "BrandId" properties
// and returns the value of the "BrandId" property casted to long
private static readonly Dictionary<Type, Func<object, long>> _getBrandId = new();
public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass,
List<long> distinctBrandIds)
where T : class
{
// Check if the function is already cached
if (!_getBrandId.TryGetValue(typeof(T), out var getBrandId))
{
// Create the lambda using an expression tree
var lambda = Expression.Lambda<Func<object, long>>(
Expression.Convert(
Expression.Property(
Expression.Parameter(typeof(object)),
"BrandId"),
typeof(long)),
target);
// Store it to the cache
getBrandId = _getBrandId[typeof(T)] = lambda.Compile();
}
distinctBrandIds.ForEach((v) =>
{
if (BrandYesNo[v]) continue;
CollectionOfSomeClass.RemoveAll(rd => getBrandId(rd) == v);
});
}
I am trying to iterate through Classes who have properties not linked to other Classes (i.e.
public SomeOtherClass code { get; set; }
In my attempt and thanks to other people's advice, I came up with the following, which iterates through the classes of a specific namespace (i.e. Library = "ConsoleApp1.Library" etc) as:
var classes = AppDomain.CurrentDomain.GetAssemblies ()
.SelectMany (t => t.GetTypes ())
.Where (t =>
t.IsClass &&
t.Namespace == Library &&
!t.IsNested)
.ToList ();
and getting properties as
var properties = classes.SelectMany (x => x.GetProperties (BindingFlags.Public | BindingFlags.Instance)
.Select (y => y.PropertyType));
while I get what I need from a foreach loop:
foreach ( var method in classes.Where
(x => !properties.Contains(x) && !properties.Contains (x))
.Select (x => x.Name) )
{
// some work here
}
However, some cases slipped through my selection; cases where the class has the below Properties as Array, ICollection, List and Dictionary:
public class TopObject
{
[JsonProperty("ex")]
public OtherResults ex { get; set; }
[JsonProperty ("Results")]
public OtherResults Results { get; set; }
private readonly OtherResults[,] Codes1 = new OtherResults[9, 9];
public ICollection<OtherResults> Codes2 { get; set; }
public List<OtherResults> Codes3 { get; set; }
public Dictionary<int, OtherResults> Map { get { return _map; } }
public TopObject()
{ Results = new OtherResults (); }
}
public class OtherResults
{
[JsonProperty ("Jcodes")]
public string Jcodes { get; set; }
public OtherResults()
{ }
}
I need help in editing the var method, to include (additionally) the cases where a property has Dictionary Value type any of the classes, or is Array of type of any of the classes, or is ICollection or List who accept any knows classes.
Kindly someone can help me with this thing? Thank you very much
So I finally had time to play around with this a bit. If I understand correctly you would like something like this:
First we create a helper function that extracts all referenced types:
public static IEnumerable<Type> GetReferencedTypes(Type type)
{
if (type.IsArray)
return new List<Type> { type, type.GetElementType() };
if (type.IsGenericType)
return type.GetGenericArguments().SelectMany(GetReferencedTypes)
.Union(new List<Type>{type});
return new List<Type>{ type };
}
I can't guarantee that this covers everything but atleast it seems to get all the references from your examples. Array is special here, it's not a generic but we have to extract it's underlying type. All generic types we extract recursively. and if it's not any of those we just return the type that was given.
So with this helper function we can now do something like this:
var currentAssembly = typeof(Program).Assembly;
var currentAssemblyName = typeof(Program).Assembly.GetName().Name;
var types = currentAssembly.GetTypes();
var classes = types.Where(type => type.IsClass && !type.IsNested).ToList();
var referencedTypes = classes.SelectMany(c => c.GetProperties().SelectMany(p => GetReferencedTypes(p.PropertyType))).Where(type => type.Assembly.GetName().Name == currentAssemblyName).Select(type => type.Name)
.Distinct().ToList();
We get the current assembly. Get all types, then put all properties of those types through our helper function, select their name, remove duplicates and create a list. Which results in:
["Method100Response201_01", "Method600Response100", "Method700Response100", "Method100Response404_01"]
Keep in mind that in your fiddle change the public Method500Response100[,] Codes1 = new Method500Response100[9, 9]; is not returned by the GetProperties() call (it has no { get; set; }).
Hope this helps
here is code illustration
interface IObjectA
{
int Id { get; }
string Name { get; }
}
class ObjectA : IObjectA
{
public int Id { get; set; }
public string Name { get; set; }
public ObjectA(int id, string name)
{
Id = id;
Name = name;
}
}
There are two ways for me to generate List<IObjectA> from some other objects
First one is using forloop:
IList<IObjectA> list = new List<IObjectA>();
foreach(var item in someList)
{
list.Add(new ObjectA(item.Id, item.Name));
}
This works perfectly fine.
Then I tried with linq
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).ToList();
The compiler will throw me a error basically saying cannot convert ObjectA to IObjectA
To make it work, i have to add
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).Cast<IObjectA>().ToList();
Can some one explain why the compile would complain?
Thanks in advance!
The problem is that the linq expressions result in a List<ObjectA>. If you can treat this result as a List<IObjectA>, the compiler might let you add hypothetical OtherObjectA objects to the list, which would blow up on you if you ever tried to cast back to the original List<ObjectA> type, which should be allowed.
To get around this, you can .Cast() the elements before calling .ToList() to get a list of the correct type:
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).Cast<IObjectA>().ToList();
You could also use the var keyword:
var list = someList.Select(c => new ObjectA(c.Id, c.Name)).ToList();
But this will still result in a List<ObjectA> and I suspect you need the List<IObjectA> for code further on.
I've read somewhere a comment by the author of ProtoBuf.NET that:
There are options to automatically infer the numbers, but that is brittle and not recommended. Only use this if you know you never need to add more members (it orders them alphabetically, so adding a new AardvarkCount will break everything).
This is exactly that sort of situation I am interested in :)
I have something that is akin to a map-reduce scenario where I want to serialize results generated on remote machines using protocol buffers (e.g. the "map" side of map-reduce) and later read them and combine those results for further processing (e.g. the "reduce" side).
I don't want to start an attribute decoration marathon over every possible class I have that might get serialized during this process, and I do find the protocol buffers to be very alluring as I can create result with Mono and consume them effortlessly on MS.NET and vice-versa...
The apparent downsides of not pre-tagging the members doesn't bother me as exactly the same software revision does generation/consumptionn, so I don't need do worry about new members popping up in the code and messing my whole scheme...
So in short, my question is:
How do I do it (Serialize with ProtoBuf.NET without tagging/building Meta classes on my own)?
Is there any hole in my scheme that I've glaringly missed?
If you can live with a single attribute, then the trick is:
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class WithImplicitFields
{
public int X { get; set; }
public string Y { get; set; }
}
there are 2 options here; AllPublic works like XmlSerializer - public properties and fields are serialized (using the alphabetic order to choose tag numbers); AllFields works a bit like BinaryFormatter - the fields are serialized (again, alphabetic).
I can't remember if this is yet available on the v2 API; I know it is on my list of things to ensure work! But if you want it in v2 without any attributes, I'm sure I can add an Add(ImplicitFields) overload.
As long as the 2 ends are never out of step, this is fine. If you store the data, or don't version the two ends "in step", then there could be problems. See also the intellisense comments on the enum (which pretty much repeats the warning that you are already aware of).
I had the same problem and that how I've resolved it with TypeModel. It's based on properties ordered by their name (however it doesn't check on property setter/getter existence or serialize-ability of a given type):
[TestFixture]
public class InferredProtoPoc
{
[Test]
public void UsageTest()
{
var model = TypeModel.Create();
// Dynamically create the model for MyPoco
AddProperties(model, typeof(MyPoco));
// Display the Generated Schema of MyPoco
Console.WriteLine(model.GetSchema(typeof(MyPoco)));
var instance = new MyPoco
{
IntegerProperty = 42,
StringProperty = "Foobar",
Containers = new List<EmbeddedPoco>
{
new EmbeddedPoco { Id = 12, Name = "MyFirstOne" },
new EmbeddedPoco { Id = 13, Name = "EmbeddedAgain" }
}
};
var ms = new MemoryStream();
model.Serialize(ms, instance);
ms.Seek(0, SeekOrigin.Begin);
var res = (MyPoco) model.Deserialize(ms, null, typeof(MyPoco));
Assert.IsNotNull(res);
Assert.AreEqual(42, res.IntegerProperty);
Assert.AreEqual("Foobar", res.StringProperty);
var list = res.Containers;
Assert.IsNotNull(list);
Assert.AreEqual(2, list.Count);
Assert.IsTrue(list.Any(x => x.Id == 12));
Assert.IsTrue(list.Where(x => x.Id == 12).Any(x => x.Name == "MyFirstOne"));
Assert.IsTrue(list.Any(x => x.Id == 13));
Assert.IsTrue(list.Where(x => x.Id == 13).Any(x => x.Name == "EmbeddedAgain"));
}
private static void AddProperties(RuntimeTypeModel model, Type type)
{
var metaType = model.Add(type, true);
foreach (var property in type.GetProperties().OrderBy(x => x.Name))
{
metaType.Add(property.Name);
var propertyType = property.PropertyType;
if (!IsBuiltinType(propertyType) &&
!model.IsDefined(propertyType) &&
propertyType.GetProperties().Length > 0)
{
AddProperties(model, propertyType);
}
}
}
private static bool IsBuiltinType(Type type)
{
return type.IsValueType || type == typeof (string);
}
}
public class MyPoco
{
public int IntegerProperty { get; set; }
public string StringProperty { get; set; }
public List<EmbeddedPoco> Containers { get; set; }
}
public class EmbeddedPoco
{
public int Id { get; set; }
public String Name { get; set; }
}
And that's what you get from running it.
message EmbeddedPoco {
optional int32 Id = 1;
optional string Name = 2;
}
message MyPoco {
repeated EmbeddedPoco Containers = 1;
optional int32 IntegerProperty = 2;
optional string StringProperty = 3;
}
For performance, you could opt to compile the TypeModel, and/or store the generated proto for future uses. Beware however that hidden dependency on Protocol Buffer could be dangerous in the long run if the poco (Plain old container object) evolves.
i have .net 3.5 and i would like to make a generic method. how do i refactor this code?
case (int)Enums.SandwichesHoagies.Cheeses:
if (this.Cheeses.Where(x => x.Id == product.ProductId).SingleOrDefault() == null)
{
var newCheese = new Cheese
{
Id = product.ProductId,
Name = product.Name,
PriceValue = product.Price.HasValue ? (double)product.Price.Value : 0.00
};
this.Cheeses.Add(newCheese);
}
else
{
foreach (var cheese in this.Cheeses.Where(cheese => cheese.Id == product.ProductId))
{
this.Cheeses.Remove(cheese);
break;
}
}
foreach (var cheese in Cheeses) cheese.Type = string.Empty;
if (this.Cheeses.Count > 0) Cheeses.First().Type = "Cheeses:";
break;
case (int)Enums.SandwichesHoagies.Meats:
if (this.Meats.Where(x => x.Id == product.ProductId).SingleOrDefault() == null)
{
var newMeat = new Meat
{
Id = product.ProductId,
Name = product.Name,
PriceValue = product.Price.HasValue ? (double)product.Price.Value : 0.00
};
this.Meats.Add(newMeat);
}
else
{
foreach (var meat in this.Meats.Where(meat => meat.Id == product.ProductId))
{
this.Meats.Remove(meat);
break;
}
}
foreach (var meat in Meats) meat.Type = string.Empty;
if (this.Meats.Count > 0) Meats.First().Type = "Meats:";
break;
Assuming two things:
Meat and Cheese inherit from Ingredient or implement IIngredient
The Meats and Cheeses collections are IList<T>
Here we go:
private void OuterMethod()
{
switch(something)
{
case (int)Enums.SandwichesHoagies.Cheeses:
HandleCase(product, this.Cheeses);
break;
case (int)Enums.SandwichesHoagies.Meats:
HandleCase(product, this.Meats);
break;
}
}
private void HandleCase<T>(Product product, List<T> list) where T : Ingredient, new()
{
if(list.Any(i => i.Id == product.ProductId))
{
list.Add(new T {
Id = product.ProductId,
Name = product.Name,
PriceValue = product.PriceValue ?? 0.0;
});
}
else
{
list.RemoveAll(i => i.Id == product.ProductId);
}
//NOTE: this part seems like a bad idea. looks like code smell.
foreach (var i in list)
{
i.Type = string.Empty;
}
if (list.Count > 0)
{
list.First().Type = "Cheeses:";
}
}
At initial glance, you have some common properties you access, Id, Name, PriceValue, and Type. That looks like a base class or interface to me. With that, you could start by refactoring your code into a method
void YourMethod<T>(List<T> list, Product product) where T : IProduct, new()
// IProduct being your interface or base class
In which case, where you refer to this.Meats or this.Cheeses, you would instead refer to list, and where you refer to instances of Meat or Cheese, you simply refer to T.
See how far that gets you and refactor further.
Hard to know your exact requirements without knowing the types (and base types/interfaces) used. I'm going to assume you're using some kind of ORM which spits out partial classes anyway.
First requirement to make this easy to work with, is that Meat and Cheese share a common interface (or abstract class). This should be something basic like this.
interface IProduct {
int Id { get; set; }
String Name { get; set; }
Double PriceValue { get; set; }
}
The assumption that partial classes are used makes it easy to extend your class to use this interface.
partial class Cheese : IProduct { }
I find it interesting that you have a different kind of Product which has different field names, but almost the same features. Should you perhaps keep the names the same as the above interface and make this also derived from the interface? Anyway, assuming what you have is something like this.
class Product {
int ProductId { get; set; }
String Name { get; set; }
Price? Price { get; set; }
}
The first thing you wanna do is use the Factory Pattern to create a specific product.
public class ProductFactory {
public T Create<T>(Product product)
where T : IProduct, new() {
return new T {
Id = product.ProductId,
Name = product.Name,
PriceValue = product.Price.HasValue
? (double)product.Price.Value
: 0.00
};
}
}
The new() constraint requires a parameterless constructor in your Cheese and Meat classes. I assume this is no problem. You just need to call .Create<Cheese>(product);
The next parts, I would need to assume that your Cheeses and Meats objects (or properties), also share a common class (ICollection<IProduct>), or you could define your own for particular needs.
public class ProductCollection : ICollection<IProduct> { ... }
A generic method to check if a product exists
Boolean ContainsProduct<T>(ProductCollection<T> collection, Product product)
where T : IProduct {
return collection.Where(x => x.Id == product.Id).SingleOrDefault != null;
}
I'm skeptical of your idea of calling .Remove inside a foreach loop. Modifying a collection can cause problems for the enumerator being used to loop through it. I would find a better approach to that if it's a concern.