This question already has an answer here:
Select template
(1 answer)
Closed 9 years ago.
I would like to apply the same SELECT to an amount of queries, how do i do this? I'm looking to make some kind of template i'm guessing?
var query = (from b in db.routes select new
{ name = b.name,
age = b.age});
I would like to predefine name=b.name and age = b.age.
Thanks
You can create a method with an IEnumerable<SomeBaseClassOrInterfacee> argument.
then you can do your select against given argument within the method.
public class Generic
{
protected Generic(string name, int age)
{
Name = name;
Age = age;
}
public string Name { get; private set; }
public int Age { get; private set; }
}
public class Human : Generic
{
public Human(string name, string surname, int age) : base(name, age)
{
Surname = surname;
}
public string Surname { get; private set; }
}
public class Pet : Generic
{
public Pet(string name, int registrationCode, int age)
: base(name, age)
{
RegistrationCode = registrationCode;
}
public int RegistrationCode { get; private set; }
}
static void Main(string[] args)
{
IEnumerable<Pet> pets = new List<Pet>();
IEnumerable<Human> palls = new List<Human>();
var resPets = SelectAgeGreaterThen10<Pet>(from p in pets where p.Name.StartsWith("A") select p);
var resHumans = SelectAgeGreaterThen10<Human>(from p in palls where p.Name.StartsWith("B") select p);
}
private static IEnumerable<T> SelectAgeGreaterThen10<T>(IEnumerable<Generic> source) where T : Generic
{
return from s in source where s.Age > 10 select (T)s;
}
The tricky bit with your example is that you're using an anonymous type - which means you can't write a method (you can't declare the return type) and you can't assign a lambda expression to a local variable (you need to be able specify a type to convert the lambda expression to).
You also can't just use type inference to return something from a generic method - as you wouldn't be able to specify just the input type. However, you can use type inference with a generic class:
public static class Functions<T>
{
public static Func<T, TResult> Create<TResult>(Func<T, TResult> func)
{
return func;
}
}
Then you can write:
var projection = Functions<Route>.Create(r => new { r.name, r.age });
var query = db.routes
.Select(projection)
...;
But if you really want to use the same projection in multiple places, you should consider creating a named result type instead - at which point you can use any of the other options, including a conversion method.
How's this look:
class NameAndAge
{
public String Name;
public Int32 Age;
}
class Whatever
{
public IEnumerable<NameAndAge> GetNameAndAges(IEnumerable<dynamic> enumerable)
{
return from b in enumerable select new NameAndAge { Name = b.name,
Age = b.age};
}
}
You'll probably want to replace dynamic in the argument type with whatever the type of the elements in db.routes are.
Related
I have the following class
public class School
{
public List<Student> Students { get; set; }
public List<Teacher> Teachers { get; set; }
}
Now i have this method
public bool Evaluate(??)
{
var school = DbContext.Schools.FirstOrDefault();
return school.??.Any(/*some expresions*/)
}
I should be able to pass a value in ?? and use it so that i can use both
return school.Students.Any(/*some expresions*/)
return school.Teachers.Any(/*some expresions*/)
So how can i replace the question marks with Students or Teachers ?
Edit:
public class Student
{
public string FullName { get; set; }
public bool Registered { get; set; }
public bool Passed { get; set; }
}
public class Teacher
{
public string FullName { get; set; }
public bool CanEvaluate { get; set; }
public bool Validator { get; set; }
}
public class DynamicCheckTest
{
public bool MyExpression<T>(List<T> items, string name,
Expression<Func<T, bool>> expression)
{
return items.Any(x => expression.Compile()(x));
}
}
public static bool Check<T>(this List<T> items, Func<T, bool> compiledExp)
{
return items.Any(x => compiledExp(x));
}
Students.Check(x => x.Name == "Mike" && x.Registered); // example
Teachers.Check(x => x.Name == "Jack" && x.CanEvaluate);// example
Now i have to pass the school along which contains both Students and Teachers
But i don't know which one will be called in advance
You could use this method:
public bool Evaluate<T>(Func<School, List<T>> project, Func<T, bool> filter)
{
var school = DbContext.Schools.FirstOrDefault();
return project(school).Any(filter);
}
If we assume that the implementation of Student and Teacher are this:
public class Student
{
public string Name;
}
public class Teacher
{
public string Subject;
}
Then you could do this:
bool hasFred = Evaluate(school => school.Students, student => student.Name == "Fred Nerk");
bool teachArt = Evaluate(school => school.Teachers, teacher => teacher.Subject == "Art");
Addressing the "Pass property name as parameter" request, you could use reflection for that, but I don't think that's a good way to go. Instead, a Func<School, List<TElement>> could be used to select the desired List<> property to evaluate...
public bool Evaluate<TElement>(Func<School, List<TElement>> listSelector)
where TElement : Person
{
School school = DbContext.Schools.FirstOrDefault();
DateTime today = DateTime.Today;
return listSelector(school)
// For example, check if today is the birthday of anyone in the selected list
.Any(person => person.DateOfBirth.Month == today.Month && person.DateOfBirth.Day == today.Day);
}
As #Enigmativity points out, the type constraint is necessary in order to pass much of a meaningful condition to Any(), which also assumes/requires that Student and Teacher have common ancestry, like this...
public abstract class Person
{
public DateTime DateOfBirth
{
get;
}
}
public class Student : Person
{
}
public class Teacher : Person
{
}
You'd then use a lambda expression to specify the desired List<>...
bool isAnyStudentsBirthday = Evaluate(school => school.Students);
bool isAnyTeachersBirthday = Evaluate(school => school.Teachers);
This will work as long as the members you want Any() to consider are available in the constrained type (i.e. Person). If you wanted to filter using members specific to the Student or Teacher class, your best bet would be to use an approach like #Enigmativity's answer, where the filter itself is a parameter and receives the same derived type as the selected List<> stores.
Note that if you ever want to use Evaluate() with some other collection property of School that is not specifically List<>, or just knowing that all Any() needs is an IEnumerable<>, you could change the return type (last type parameter) of the Func<> to something less-restrictive...
Func<School, IList<TElement>>
Func<School, ICollection<TElement>>
Func<School, IEnumerable<TElement>>
I know how to cast implicity object to string but is it possible to cast string to object property?
I would like to fill object property directly, without using code: ob.property="text".
Instead I would like to use: ob="text"
Example:
class Person
{
public string Name { get; set; }
public string Name2 { get; set; }
public string Name { get; set; }
.
.
.
}
var a=new Person();
I would like to fill property Name with string "text1"
Is there any way to do this : a="text1" instead of a.Name="text1" ?
I know how to cast implicity object to string but is it possible to cast string to object property?
The closest you can achieve this is using implicit operators overload.
For example,
class Person
{
public string Name { get; set; }
public static implicit operator string(Person person)
{
return person.Name;
}
public static implicit operator Person(string name)
{
return new Person(){Name=name};
}
}
Now you can assign as
var person = new Person();
person = "Test Name";
But you would need to evaluate whether this effort is worth it.
While it is a stupid idea, you could use a bit of reflection and as #Bizhan said an indexer to achieve something similar.
static void Main(string[] args)
{
MyClass mc = new MyClass();
mc[0] = "Test Name";
mc[1] = "Test Surname";
}
class MyClass
{
public string this[int i]
{
set
{
var props = this.GetType().GetProperties();
props[i + 1].SetValue(this, value);
}
}
public string Name { get; private set; }
public string Surname { get; private set; }
}
The only problem with this is that it'll only work on String types, the indexer must be first in the class, exception will be thrown when property index will be outside available number of properties, it uses index based property access but that could be automated using a for loop. Then again you could modify this code to do various checks.
It still makes no reason why one would need this - if you explain your use case better there might be better ways to go on about your problem.
I need to fill a list with specific data. This list is a property of another object. The elements of that List have the following rules:
They contain a int-Property named "Id" which is writable
They have a constructor without parameters
This is the code so far: (I did not add any plausiblity checks or error handling to keep this example simple)
private void SetListProperty(string propertyName, object target, int[] ids) {
PropertyInfo property=target.GetProperty(propertyName);
Type propertyType=property.PropertyType();
Type elementType=propertyType.GetElementType();
PropertyInfo elementId=elementType.GetProperty("Id");
var targetList=new List<>(elementType); // PseudoCode. Does not work
foreach (int id in ids) {
var element=Activator.CreateInstance(elementType);
elementId.SetValue(element, id);
targetList.Add(element); // PseudoCode. Does not work
}
property.SetValue(target, targetList);
}
Example for calling that method:
public class User {
public int Id {get;set;}
public string Name {get;set;}
}
public class House {
public int Id {get;set;}
public string HouseName {get;set;]
}
public class Group {
public string Name {get;set;}
public List<User> Users {get;set;}
public List<House> Houses {get;set;}
}
var group=new Group { Name="nerds"};
SetListProperty("Users"), group, new int[] {1,2,3,4,5});
SetListProperty("Houses"), group, new int[] {1,2,3,4,5});
So after calling that method, group should contain a property Users that has 5 elements each with the Id set.
I've seen similar questions here about creating a List of an unknown Type, but not how to actually add single items of an unknown type to that list.
(Update)
I assume my problem was not clear enough in the beginning. Inside that method I do NOT know the property Type. Not even if it is a list.
I only have the property name as string.
I scrapped the original answer (below) to truly address doing this without knowing the type of property OTHER than that it is either an item that has an id...or it is an enumerable of objects that have an Id.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace StackOverflowTests
{
[TestClass]
public class StackOverflow_49181925Tests
{
public interface IHasId
{
int Id { get; set; }
}
public class Foo : IHasId
{
public int Id { get; set; }
}
public class HasAFoo
{
public Foo Foo { get; set; }
}
public class HasManyFoos
{
public IEnumerable<Foo> Foos { get; set; }
}
public void SetPropertyIds(object target, string propertyName, IEnumerable<int> ids)
{
var property = target.GetType().GetProperty(propertyName);
var propertyType = property.PropertyType;
//is it enumerable?
if (typeof(IEnumerable).IsAssignableFrom(propertyType))
{
var objectType = propertyType.GetGenericArguments().First();
var list = Activator.CreateInstance(typeof(List<>).MakeGenericType(objectType)) as IList;
foreach(var id in ids)
{
var obj = Activator.CreateInstance(objectType) as IHasId;
((IHasId)obj).Id = id;
list.Add(obj);
}
property.SetValue(target, list);
}
else
{
if(ids.Count() != 1) throw new ApplicationException("You're trying to set multiple Ids to a single property.");
var objectType = propertyType;
var obj = Activator.CreateInstance(objectType);
((IHasId)obj).Id = ids.First();
property.SetValue(target, obj);
}
}
[TestMethod]
public void TestHasAFoo()
{
var target = new HasAFoo();
this.SetPropertyIds(target, "Foo", new[] { 1 });
Assert.AreEqual(target.Foo.Id, 1);
}
[TestMethod]
public void TestHasManyFoos()
{
var target = new HasManyFoos();
this.SetPropertyIds(target, "Foos", new[] { 1, 2 });
Assert.AreEqual(target.Foos.ElementAt(0).Id, 1);
Assert.AreEqual(target.Foos.ElementAt(1).Id, 2);
}
}
}
ORIGINAL ANSWER BELOW
Lots of different ways of accomplishing this. The third way which implements an interface can give you a lot of help since most of your business models probably have some sort of Id. The Linq version though is short and sweet. These are all just variations on a theme.
Using Linq:
group.Users = new[]{1,2,3}.Select(s=>new User{Id = s}).ToList();
Using Generics:
private IList<T> IntsToModels<T>(IEnumerable<int> ints, Action<T,int> setId) where T : new(){
return ints.Select(s=>{
var t = new T();
setId(t,s);
return t;
}).ToList();
}
Using Generics and Interfaces
public interface IHasID{
int Id{get;set;}
}
//implement the IHasID interface
public class User : IHasID...
private IEnumerable<T> ToModels(IEnumerable<int> ints) where T : IHasID, new(){
foreach(var i in ints){
yield return new T{Id = i};
}
}
I want to say that I am learning the language with simple coding.
If I create an object ResourceTemplate as
public class ResourceTemplate
{
public string Name;
public int Value;
}
And then made
List<ResourceTemplate> resource = new List<ResourceTemplate>();
That resource will be a list with the structure from ResourceTemplate.
If that's so, why I can not use resource.Add("product", 500);?
The compiler says that:
it can't do an overload with the .Add() function
.
You should create a new instance of the class:
resource.Add(new ResourceTemplate {Name = "product", Value = 500});
Or like this:
ResourceTemplate resourceTemplate = new ResourceTemplate
{
Name = "product",
Value = 500
};
resource.Add(resourceTemplate);
However by adding a constructor to your class like this:
public class ResourceTemplate
{
public ResourceTemplate(string name, int value)
{
Name = name;
Value = value;
}
public string Name;
public int Value;
}
You can then:
resource.Add(new ResourceTemplate("product", 500 ));
Also I'd suggest that you use properties instead of public fields:
public string Name { get; set; }
public int Value { get; set; }
List<T>.Add expects an instance of the specified type, not a list of arguments that would create such a type.
For example (using the object initializer syntax):
resource.Add(new ResourceTemplate {Name = "product", Value = 500});
You can also use the collection initializer syntax additionally:
var resource = new List<ResourceTemplate>() {{Name = "product", Value = 500}};
You can use a collection base structure.
public class ResourceTemplate
{
public string Name;
public int Value;
}
public class ResourceTemplateList : System.Collections.CollectionBase
{
public void Add(string name, int value)
{
this.List.Add(new ResourceTemplate { Name = name, Value = value });
}
}
Then, you can add class members to the list.
ResourceTemplateList r_templates = new ResourceTemplateList();
r_templates.Add("product", 500);
By default: resource.Add can only accept a ResourceTemplate as parameter.
The correct syntax must be:
var resourceTemplate = new ResourceTemplate
{
Name = "product",
Price = 500,
};
resource.Add(resourceTemplate);
You can't do that because there is no version of Add that can accept a string and an integer and convert to an unknown ResourceTemplate structure.
In order to add an overload of Add you can create an extension method:
public static class ListExtensions
{
public static void Add(this List<ResourceTemplate> list, string name, string price)
{
var resourceTemplate = new ResourceTemplate
{
Name = name,
Price = price,
};
list.Add(resourceTemplate);
}
}
So far those are the main concepts (and solution) that i solved in this question:
//Declarations of Lists
public List<ResourceTemplate> stockpile = new List<ResourceTemplate>();
//We pass to the obj a name and creates the obj with the name and a
stockpile
public NationBuilder(string name)
{
this.Name = name;
stockpile = new List<ResourceTemplate>();}
//We can add resources into the stockpile with LINQ
public void AddResource(string itemName, int quantity, float value)//Adds
Resources to the stockpile
{
stockpile.Add(new ResourceTemplate {Name = itemName, Quantity =
quantity, Value = value });
}
The output of this solution is this data structure:
- nation
- Name
- Stockpile
-[0] <-Here
- Name "resourceName"
- Quantity "quantity"
- Value "value"
Here: I would like to have instead of indexing via in, to index via a Name.
Instead of
[0] -> [Coal]
- Quantity
- Value
Otherwise the handler that will manage the stockpiles will need to know beforehand where each resource will be located.
I'm beginning to fall in love with Extension Methods, but I just don't know how to create an EM only for a determinate Object type.
I have for example:
public static void AddPhoneNumberToContact(this Contact contact, PhoneType type, String number)
{
lock (contact)
{
PhoneRow pr = PhoneRow.CreateNew();
pr.SetDefaults();
pr.PtypeIdx = type;
pr.PhoneNumber = number;
contact.Phones.Add(pr);
pr = null;
}
}
My problem is that I want to also Have this method in the Person object, and that is why I named
AddPhoneNumberToContact
AddPhoneNumberToPerson
Is there a way to have AddPhoneNumber and deal with the object that is provided?
or the solution is to have
public static void AddPhoneNumber(this object contact, ...
{
...
if(typeof(Contact) == contact)
((Contact)contact).Phones.Add(pr);
else if(typeof(Person) == contact)
((Person)contact).Phones.Add(pr);
}
Thank you.
How about writing two extension methods:
public static void AddPhoneNumber(this Contact contact, PhoneType type);
and
public static void AddPhoneNumber(this Person person, PhoneType type);
Looks cleaner to me.
If there's some common code between the two, extract that into a separate method.
Make Contact and Person implement common interface - say IContactWithPhoneNumbers - and then write an extension method "for this interface".
public interface IContactWithPhoneNumbers {}
public class Contact : IContactWithPhoneNumbers {}
public class Person : IContactWithPhoneNumbers {}
public static void AddPhoneNumber(this IContactWithPhoneNumbers obj) {}
Reading your comments (objects are from an SDK and are not editable). I would probably do something like this:
public class Util
{
//common util method
public static void AddPhoneNumber(object obj, string phoneNumber)
{
if(obj is Contact)
((Contact)contact).Phones.Add(phoneNumber);
else if(obj is Person)
((Person)contact).Phones.Add(phoneNumber);
}
//extension method for Person
public static void AddPhoneNumber(this Person p, string phoneNumber)
{
AddPhoneNumber((object)p, phoneNumber);
}
//extension method for Contact
public static void AddPhoneNumber(this Contact c, string phoneNumber)
{
AddPhoneNumber((object)c, phoneNumber);
}
}
I do think the best practice though when you have control of the underlying objects would be to implement a common interface.
You might make your extension method generic, e.g.:
public static void AddPhoneNumberToContact<T>(
this T contact,
PhoneType type,
String number
)
{
PhoneRow pr = PhoneRow.CreateNew();
pr.SetDefaults();
pr.PtypeIdx = type;
pr.PhoneNumber = number;
((T)contact).Phones.Add(pr);
pr = null;
}
You won't be able to use lock because "'T' is not a reference type as required by the lock statement", so you might have to return some value.
If it complains about not being able to resolve the Phones method on type T, you could:
Pass in some function delegate that would take type T, return nothing, and perform the action ((T)contact).Phones.Add(pr);.
Or you could create an interface like the following:
public interface IPhoneable
{
IList<Phone> Phones();
}
Then, once you have that interface, you can add the following to your generic extension method:
public static void AddPhoneNumberToContact<T>(
this T contact,
PhoneType type,
String number
) where T : IPhoneable {...}
Here, T is still a generic type, but now your AddPhoneNumberToContact method has the requirement that, whatever T is, it inherits from the IPhoneable interface, which you just defined to have the Phones() method.
See also C# Extension Method for Generic Collections.
If you can not change Person and Contact you can create a subclass of them and let them implement the common interface.
In the extension method you declare the common interface as parameter:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var p = new MyPerson();
p.Name = "test";
p.AddPhonenumber("555-2356");
Console.WriteLine(string.Join(", ", p.Phonenumber));
var c = new MyContact();
c.Name = "contact";
c.AddPhonenumber("222-235");
Console.WriteLine(string.Join(", ", c.Phonenumber));
}
}
public class Contact
{
public Contact() {
this.Phonenumber = new List<string>();
}
public string Name { get; set; }
public List<string> Phonenumber { get; set; }
public string Foo { get; set; }
}
public class Person
{
public Person() {
this.Phonenumber = new List<string>();
}
public string Name { get; set; }
public List<string> Phonenumber { get; set; }
public string Bar { get; set; }
}
public class MyContact: Contact, IType {
}
public class MyPerson: Person, IType {
}
public static class Extensions {
public static void AddPhonenumber(this IType type, string number){
type.Phonenumber.Add(number);
}
}
public interface IType {
string Name {get; set; }
List<string> Phonenumber {get; set;}
}