c# methods that takes an Interface? - c#

I am new to Interfaces.
I have a lot of objects that I pass as DTOs through my layers to the UI. Some of them are quite complex (Quite a few properties), but I only want to use them, in certain circumstances, in DropDown lists. These DTOs all have an int Id, and a string Description.
I would like to create a static function that takes a List<> of one of these objects, and returns a List<SelectListItem>
So, I am trying to use Interfaces for the first time.
I created an Interface:
public interface IListableItem
{
int Id { get; set; }
string Description { get; set; }
}
And then, I assigned that interface to one of my DTO objects I am trying to convert:
public class CategoryDto : BaseDto , IListableItem
{
public int PortfolioId { get; set; }
public string Description { get; set; }
public List<ExtendedSubCategoryDto> SubCategories { get; set; }
public bool IsExpenseCategory { get; set; }
public CategoryDto()
{
SubCategories = new List<ExtendedSubCategoryDto>();
}
}
Then, I created my generic method that takes a list of the category dtos, and will hopefully return a list
public static List<SelectListItem> TranslateToSelectList(List<IListableItem> source)
{
var reply = source.Select(item => new SelectListItem
{
Value = item.Id.ToString(CultureInfo.InvariantCulture), Text = item.Description
}).ToList();
return reply;
}
But, when I attempt to use this method, passing it a List, it fails.
model.Categories =
Translator.TranslateToSelectList(MyService.GetCategoriesByPortfolioId());
GetCategoriesByPortfolioId returns a List.
It's failing with the error:
CategoryDto is not assignable to IListableItem
It's probably a basic Interface understanding issue on my part, but what am I doing wrong, and how can I fix it?

If your method expects List<IListableItem>, you can't pass List<CategoryDTO>.
If that would be possible, you could Add different instances of elements that implement the interface IListableItem into a collection that is holding CategoryDTO elements, and read them.
Ultimately, that wouldn't make sense.
You can fix it, if you use IEnumerable interface. That allows covariance(going from higher type to lower type in generic type parameter).
The reason it works, is that IEnumerable is a "read-only view" of collection, thus you can't really add anything to it - plus what's important, the type parameter is marked as covariant.
public static List<SelectListItem> TranslateToSelectList(
IEnumerable<IListableItem> source)
{..}

Related

How to cast a 'list of classes' to a 'list of its interface'?

I need to cast a class list to its own interface list.
So I have interface Demo_Interface and two classes based on Demo_Interface ,
Now I create list of classes like List<Test_Class1>
And I have a function with List<Demo_Interface> parameter.
Here's interface :
interface Demo_Interface
{
int test_int { get; set; }
}
Here's Entire Code :
using System;
using System.Collections.Generic;
namespace ConsoleApp3
{
class Program
{
///// Main Interface
interface Demo_Interface
{
int test_int { get; set; }
}
//// Class 1 Based On Demo_Interface
class Test_Class1 : Demo_Interface
{
public int test_int { get; set; }
public string test_string { get; set; }
}
///// Class 2 Based On Demo_Interface
class Test_Class2 : Demo_Interface
{
public int test_int { get; set; }
public string test_string { get; set; }
}
//// And Main Class
class Main_Class
{
public List<Test_Class1> class_list_1 { get; set; }
public List<Test_Class2> class_list_2 { get; set; }
public Main_Class()
{
class_list_1 = new List<Test_Class1>() { };
class_list_2 = new List<Test_Class2>() { };
}
}
//// Console Main
static void Main(string[] args)
{
var new_main_class = new Main_Class();
Output_Class(new_main_class.class_list_1); ///// ==> ERROR
Console.ReadKey();
}
//// Simple Function for do something with interface
static void Output_Class(List<Demo_Interface> inter_input)
{
for (int i = 0; i < inter_input.Count; i++)
{
Console.WriteLine("{0} - {1}",i, inter_input[i].test_int);
}
}
}
}
How Can I cast List<Test_Class1> to List<Demo_Interface> , When Test_Class1 uses Demo_Interface?
You can try
List<Test_Class1> testDemo = new List<Test_Class1>(); //list of Test_Class1 instances
List<Demo_Interface> result = testDemo.ToList<Demo_Interface>();
This is safe because we are not directly casting testDemo to its interface. We are keeping testDemo as it is and we are creating result which is list of Demo_Interface
You cannot cast a List<ClassThatImplementsInterface> as a List<IInterfaceItImplements>.
If you could, and you did this:
var classList = new List<ClassThatImplementsInterface>();
var interfaceList = (List<IInterfaceItImplements>)classList;
... then you would be able to do this:
interfaceList.Add(new SomeOtherClassThatImplementsTheInterface);
But casting the list doesn't create a new list. In the above example there aren't two lists. There are two variables with references to the same list. If you could cast as seen above, you would be able to define a list of one type and add a completely different type to it. The compiler prevents that.
You could
create a new List<IDemoInterface> and add the items to it. (Or an array, IEnumerable, etc.)
Leave the list as-is, and just cast individual items when/if you need to. In most cases we wouldn't need to cast something as an interface it implements.
If we need to cast a whole collection as a different type, it's likely because we're passing it as an argument.
That's actually a good reason not to define a method argument as a collection type like a List<T> which can be modified unless it's our intent to modify the collection.
That's one reason why we pass less-specific collection types, like IEnumerable<T>.
Suppose the method argument looks like this:
void MethodINeedToPassTheArgumentTo(IEnumerable<IDemoInterface> items)
Now we can take our List<TestClass> and do this:
MethodINeedToPassTheArgumentTo(testClassList.Cast<IDemoInterface>);
We're not creating a new collection. We're passing a reference that allows the other method to view the items in the list, each individually cast as IDemoInterface. For practical purposes it looks to the other method like a collection of IDemoInterface, and that's okay because the other item can't modify the collection. It can't attempt to add other types into the List<TestClass>.
If you need only to enumerate through the List<Demo_Interface> like shown in example, you don't have to do any kind of explicit casting. List<T> implements IEnumerable<T> which is covariant generic type.
Covariance for collections enables implicit conversion of a collection of a more derived type to a collection of a less derived type
In your case, List<Test_Class1> implements IEnumerable<Test_Class1>, but since Test_Class1 implements Demo_Interface, you can take advantage of generics variance and write, for example, something like this:
IEnumerable<Test_Class1> col = new List<Test_Class1>();
IEnumerable<Demo_Interface> colImplicit = col;
That basically means that your Output_Class method can take IEnumerable<Demo_Interface> argument and you'll be able to pass both lists without casting them explicitly using Cast<T> or creating a new collection using ToList<T>.
private void Output_Class(IEnumerable<Demo_Interface> inter_input)
{
// do your thing
}
// Method invocation
Output_Class(new_main_class.class_list_1);

Cast Action<T1> to Action<T2> in c# where T1 and T2 have no relation

I've got two objects which (Domain and Data) which in this case have the same property (let's presume Name). I've got an Action<DomItem> which I would like to cast to Action<DataItem>.
public class DomItem {
public string Name { get; set; }
}
public class DataItem {
public string Name { get; set; }
}
public class Program {
public Program() {
Action<DomItem> domAction = new Action<DomItem>(x=>x.Name = "Test");
// Something Casted To Action<DataItem>(x=>x.Name = "Test");
}
}
Of course this is just a basic example. It's by design that I can NOT use a common interface. I do not care about the DataItem might not be having the same property.
I've been looking into Expressions and several other solutions but I just can't figure out how to create the Cast (or get the "x=>x.Name =..." part from the method).
Any help would be really appreciated!
You can't directly or indirectly cast a Action<DomItem> to an Action<DataItem>, but you could wrap the action with a converter that converts the input from a DataItem to a DomItem and runs the original action on the copy:
public Action<DataItem> Convert(Action<DomItem> action)
{
return new Action<DataItem>(o => action(Map(o)));
}
public DomItem Map(DataItem dataItem)
{
return new DomItem{Name = dataItem.Name};
}
The obvious downside is that the action will be applied to a copy of the original object and not the original object itself. Without knowing exactly what the action is I don't know of a way to "cast" the action without a common base type.

How to create a list of dictionary objects of unknown value type?

I'm creating a CInformation class that will include various types of information. One type of the information it will expose are Parameters. Each parameter can be typed with any of the following types: int, short, string. Additionally, any parameter may have several possible values based on a string key. So I thought of creating a Dictionary<string, T> to hold all possible values for a parameter, but the problem arises when I try to declare my Parameters list. I created the following classes:
public class CParameter<T>
{
public object ParameterType { get; set; }
public Dictionary<string,T> ValueByString;
}
public class CInformation
{
public string Version { get; set; }
public string Name{ get; set; }
public List<CParameter<object>> Parameters; // cannot cast any of my types to object, obviously!
}
Any suggestions how I could get around my issue? I open to different solutions to my problem, not necessarily the same design I have above. Thank you.
EDIT: The main feature I want to achieve is to be able to have a list of dictionaries of different value types.
Using object to specialize a generic type is rather suspicious. If you do that, you might as well not even use a generic type at all. :-)
I think the issue here is that you want your instances of CParameter<T> to be specialized for different parameter types, and you want the parameters list on the CInformation class to contain different kinds of CParameter<T>.
In other words:
namespace Scratch
{
class Program
{
static void Main(string[] args)
{
CParameter<int> ints = new CParameter<int>();
CParameter<long> longs = new CParameter<long>();
CInformation info = new CInformation();
info.AddParameter(ints);
info.AddParameter(longs);
CParameter<int> ints2 = info.GetParameter<int>();
// ints2 and ints will both refer to the same CParameter instance.
}
}
public class CParameter<T>
{
public Type ParameterType { get { return typeof(T); } }
public Dictionary<string, T> ValueByString;
}
public class CInformation
{
public string Version { get; set; }
public string Name { get; set; }
private List<object> parameters;
public CInformation()
{
this.parameters = new List<object>();
}
public void AddParameter<T>(CParameter<T> parameter)
{
this.parameters.Add(parameter);
}
public CParameter<T> GetParameter<T>()
{
foreach (object parameter in this.parameters)
{
if (parameter is CParameter<T>)
return (CParameter<T>)parameter;
}
throw new Exception("Parameter type " + typeof(T).FullName + " not found.");
}
}
}
Note that the List<object> in that example could just as well be an ArrayList.
Also, I have a hunch that you're going to want to retrieve those CParameter objects by name rather than just by type. So consider adding a Name property to CParameter, and a name parameter to the GetParameter method. Then iterate over the list to find the property with the right name. Casting the result before returning it will verify that the type is the one you expected.
Or better yet, store the parameters in a Dictionary<string,object> instead of just a list, and use the parameter names as keys.

Relate two lists with LINQ extensions

I have two lists of different objects, one from a third party API and one from my database - and I'm trying to link the two as a relationship. Ideally with a similar effect of how DBML's create relationships for tables with foreign keys (Customer.Orders).
From third party:
class ApiObject {
public string ID { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
... 30 other properties ...
}
From my database:
class DbmlObject {
public int ID { get; set; }
public string ApiID { get; set; }
public string OtherString { get; set; }
}
They are related through ApiObject.ID == DbmlObject.ApiID
I do not want to merge these, nor join them into some anonymous object (and explicitly list 30+ properties) - but rather to make the DbmlObject a linked property of ApiObject. i.e.: addressable as:
apiObject.DbmlObjects.First().OtherString or ideally apiObject.DbmlObject.OtherString since it is a 1 to 1 relationship.
In controller:
List<ApiObject> apiObjects = _thirdParty.GetObjects();
DbmlDataContext model = new DbmlDataContext();
List<DbmlObject> dbmlObjects = model.GetAllDbmlObjects();
// relate them here
foreach (var apiObject in apiObjects)
Console.Write(apiObject.DbmlObject.OtherString)
// NOTE: ideally this foreach loop should not make a DBML query on each iteration, just the single GetAllDbmlObjects query above.
It sounds like a join:
var combined = from api in apiObjects
join dbml in dbmlObjects on api.ID equals dbml.ApiID
select new { api, dbml }
In order to get DbmlObject "in" the ApiObject, you will need to either inherit ApiObject and construct a new one of that class, which includes the Dbml property, or create a entirely new class to return. If you need static typing this is the best you can do - of course you could (mis)use dynamic to get what you want.
In this case, you are mentioning (in comments) that the ApiObject class is from a third party library that you can't change - in this case I would probably choose to create a new type which takes an instance of both objects in the constructor and exposes the properties you need - a decorator. Yes, it looks like a lot of code, but it is not complex, good tools will autogenerate it for you - and you get the class that you need for your code to be succinct.
In case you want to go further with returning an IEnumerable<dynamic>, you could build a "combining dynamic" object based on DynamicObject that then responds to all the properties of ApiObject and DbmlObject - or just adds DbmlObject as a property. I am not saying this is the right way to go, it depends on what you need it for - remember you are losing type safety. Here is a simple example:
void Main()
{
dynamic dyn = new CombiningDynamic(new Foo { X = 3 }, new Bar { Y = 42 });
Console.WriteLine(dyn.X);
Console.WriteLine(dyn.Y);
}
public class Foo
{
public int X {get;set;}
}
public class Bar
{
public int Y { get;set;}
}
public class CombiningDynamic : DynamicObject
{
private object [] innerObjects;
public CombiningDynamic(params object [] innerObjects)
{
this.innerObjects = innerObjects;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
foreach(var instance in innerObjects)
{
Type t = instance.GetType();
PropertyInfo prop = t.GetProperty(binder.Name);
if (prop != null && prop.CanRead)
{
result = prop.GetValue(instance, null);
return true;
}
}
result = null;
return false;
}
}
Remember, this is example code. If you really go this way, you would want to perhaps override some more of the methods (TrySetMember, ...), and you most definetely would want to cache the reflection results so you don't need to walk the types each time - reflection is (comparatively) slow.

Use only one method that accepts different typed parameters

I've 3 different database tables that have the same 5 fields but those does not have any foreign key relation as they are not keeping the same value in fact, but the equivalents; like: CompanyA table has productA and CompanyB has productB.
so I have 3 different collections include 3 fields that are equivalent. So what I'd like to do is to use a single class that has companyType and ProductName properties and use only one method to cast those 3 different collections to one and only class object, say ResultClass.
public class ResultClass
{
public EnumCompanyType CompanyType { get; set; }
public string ProductName { get; set; }
public ICollection<ResultClass> ConvertAnything(ICollection<T> collection)
{
//Cast it to ResultClass
return resultClassCollection;
}
}
So that I can use this like:
ICollection<ProductA> aCollection = GetCompanyAData();
ICollection<ProductB> bCollection = GetCompanyBData();
ConvertAnything(aCollection);
ConvertAnything(bCollection);
I've tried "dynamic" but actually don't know the principle (neither have the knowledge); so I've messed it up and I think it's not for this stuff.
I've tried to create an extension method but since the extension has no type for its parameter (as it is using ICollection), I can't access the fields of the items (eg. properties)
I'm using LinqToSql and all the database table terms etc. belongs to this concept, nothing else.
edit:
I think I should made myself clear:
The multiple instances that I'm trying to avoid (or shouldn't I, still thinking) is like below
public ICollection<ResultClass> ConvertAnythingForA(ICollection<ProductA> collection)
{
foreach(var item in collection)
{
var result = new ResultClass
{
ProductName = item.ProductA,
ProductType = EnumProductType.ProductA
};
resultClassCollection.Add(result);
}
return resultClassCollection;
}
public ICollection<ResultClass> ConvertAnythingForB(ICollection<ProductB> collection)
{
foreach(var item in collection)
{
var result = new ResultClass
{
ProductName = item.ProductB,
ProductType = EnumProductType.ProductB
};
resultClassCollection.Add(result);
}
return resultClassCollection;
}
Thanks in advance.
I may not be understanding you completely, but since ProductA, ProductB etc have the same signature it seems like you'd want an interface like
public interface IResultClass
{
int CompanyType { get; set; }
string ProductName { get; set; }
}
And have those classes just implement the interface. You could work with collections of the interface that could have objects of the various types. If you need a convert anything method, it would look like
public ICollection<IResultClass> ConvertAnything<T>(ICollection<T> collection) where T : IResultClass
{
return collection.Select(x => (IResultClass)x).ToList();
}
After comments- I see you that you are getting a non generic ICollection. Did you try something like this:
public ICollection<IResultClass> ConvertAnything(ICollection collection)
{
var x = collection.Cast<IResultClass>();
return x.ToList();
}
You may want to use function overloading. This example uses different numbers of parameters, but you could just as easily use different types instead.
http://csharp.net-tutorials.com/classes/method-overloading/
If both datasets are equivalent, why not just have one type called ICollection<Product>? And one function, eg. "GetProductData("A")", where "A"/"B" is the parameter? Or am I missing something?

Categories

Resources