Why do I get a type conversion compile error for the following piece of code?
I have quite a few instances of derived Def/View classes in my project. All of them have some code base, say persistence, retrieval etc. I thought by writing a helper class using generics I could achieve maintainability of this common code base.
However I get 'Type Conversion' compilation error in the DoSomeStuff method on the line that assigns the view to def. I have taken care to write implicit cast conversion for all of base and derived classes.
Note that Def & View classes intentionally do not derive from some common class. Additionally I always want to convert only from View to Def and never the other way round, hence only my View classes have the implicit conversion defined on them.
I did try to follow Eric Lipert's discussion on Covariance and Contravariance, but got quite muddled up in my head as he progressed with his examples. Any help with this problem is greatly appreciated.
public class BaseDef
{
public int Id { get; set; }
}
public class DerivedDef : BaseDef
{
public string Name { get; set; }
public DerivedDef()
: base()
{
}
public DerivedDef(BaseDef bd)
{
this.Id = bd.Id;
}
}
public class BaseView
{
public int Id { get; set; }
public BaseView()
{
}
public BaseView(BaseDef bd)
{
Id = bd.Id;
}
public BaseDef ToBaseDef()
{
return new BaseDef { Id = this.Id };
}
public static implicit operator BaseView(BaseDef bd)
{
return new BaseView(bd);
}
public static implicit operator BaseDef(BaseView bv)
{
return bv.ToBaseDef();
}
}
public class DerivedView : BaseView
{
public string Name { get; set; }
public DerivedView()
: base()
{
}
public DerivedView(DerivedDef dd)
: base(dd)
{
Name = this.Name;
}
public DerivedDef ToDerivedDef()
{
return new DerivedDef(this)
{
Name = this.Name,
};
}
}
public class SomeHelper<Tdef, Tview>
where Tdef : BaseDef
where Tview : BaseView
{
public void DoSomeStuff(Tview vm)
{
Tdef df = vm; // this line give a compile error 'Cannot convert type 'Tview' to 'Tdef'
// work with df from here on
}
}
There's no guarantee that there is a conversion to Tdef. There's definitely a conversion to BaseDef, and the compiler will use that conversion:
BaseDef df = vm; // This is fine
... but that's not the same thing.
In this case, that conversion is actually going to return a BaseDef anyway - there is no conversion operator from DerivedView to DerivedDef... there's a method (ToDerivedDef) but there's nothing in your code which would call it. Even if the conversion existed in this particular case, the compiler can't guarantee that it exists.
You could use:
Tdef df = (Tdef) (BaseDef) vm;
... that would perform the user-defined conversion to BaseDef, and then a normal cast to Tdef - which would fail at execution time in your case, but could work if the conversion called an appropriate virtual method. It can't be guaranteed at compile-time though.
I really couldn't use Jon's approach as I had layering constraints. The def model(s) are defined in the DB layer while the view model(s) in the UI layer.
However, drawing inspiration from Jon's comment, how I solved the issue at hand was to add implicit conversion on all the view model(s) and exposed two properties on the helper class that handled the conversion to & fro. This is how the final code looks...
public class BaseDef
{
public int Id { get; set; }
public override string ToString()
{
return Id.ToString();
}
}
public class BaseView
{
public int Id { get; set; }
public BaseView()
{
}
public BaseView(BaseDef bd)
{
Id = bd.Id;
}
public BaseDef ToBaseDef()
{
return new BaseDef { Id = this.Id };
}
public static implicit operator BaseView(BaseDef bd)
{
return new BaseView(bd);
}
public static implicit operator BaseDef(BaseView bv)
{
return bv.ToBaseDef();
}
}
public class DerivedDef : BaseDef
{
public string Name { get; set; }
public DerivedDef()
: base()
{
}
public DerivedDef(BaseDef bd)
{
this.Id = bd.Id;
}
}
public class DerivedView : BaseView
{
public string Name { get; set; }
public DerivedView()
: base()
{
}
public DerivedView(DerivedDef dd)
: base(dd)
{
Name = this.Name;
}
public DerivedDef ToDerivedDef()
{
return new DerivedDef((BaseView)this)
{
Name = this.Name,
};
}
public static implicit operator DerivedView(DerivedDef dd)
{
return new DerivedView(dd);
}
public static implicit operator DerivedDef(DerivedView dv)
{
return dv.ToDerivedDef();
}
}
public class SomeHelper<Tdef, Tview>
where Tdef : BaseDef
where Tview : BaseView
{
public Func<Tview, Tdef> ConvertToDef { get; set; }
public Func<Tdef, Tview> ConvertToView { get; set; }
public Tdef Convert(Tview vm)
{
if (ConvertToDef == null)
{
throw new ArgumentNullException("ConvertToDef uninitialized");
}
return ConvertToDef(vm);
}
public Tview Convert(Tdef dm)
{
if (ConvertToView == null)
{
throw new ArgumentNullException("ConvertToView uninitialized");
}
return ConvertToView(dm);
}
}
The consuming code looks like this...
private static void TestCastWithGenerics()
{
BaseDef bd = new BaseDef()
{
Id = 1
};
DerivedView dv = new DerivedView()
{
Id = 1,
Name = "DV",
};
var aClassD = new SomeHelper<DerivedDef, DerivedView>();
aClassD.ConvertToDef = dv1 => dv1; // Behind scenes the implicit cast is being invoked...
DerivedDef dd = aClassD.Convert(dv);
var aClassB = new SomeHelper<BaseDef, BaseView>();
aClassB.ConvertToView = bd1 => bd1; // Behind scenes the implicit cast is being invoked...
BaseView bv = aClassB.Convert(bd);
Console.WriteLine(dd);
Console.WriteLine(bv);
}
Related
I have 2 classes:
public class GenericClass<T>
{
public T Item {get;set;}
}
public class StringClass
{
public string Item {get;set;}
}
now i have a GenericClass object and i need to cast it to StringClass object:
var genericObj = new GenericClass<string>();
var stringObj = (StringClass)genericObj; // <---
How to cast / convert from generic class to specific one?
You can't cast one type to the other because the types are unrelated.
You could, however, define a conversion operator:
public class StringClass
{
public string Item { get; set; }
public static explicit operator StringClass(GenericClass<string> generic)
=> new StringClass { Item = generic.Item };
}
Which would allow this syntax:
var genericObj = new GenericClass<string>();
var stringObj = (StringClass)genericObj;
You can't. You would need common inherited type or implement an interface.
With inheritance:
public class GenericClass<T>
{
public T Item {get;set;}
}
public class StringClass : GenericClass<string>
{
}
if your really need it, you can do this way for examle
var stringObj = new StringClass(genericObj);
public class StringClass
{
public string Item { get; set; }
public StringClass(GenericClass<string> genericClass)
{
Item=genericClass.Item;
}
public StringClass(){}
}
or this is more flexible
public interface IGenericClass<T>
{
public T Item { get; set; }
}
public class GenericClass<T>:IGenericClass<T>
{
public T Item { get; set; }
}
public class StringClass
{
public string Item { get; set; }
public StringClass(IGenericClass<string> genericClass)
{
Item=genericClass.Item;
}
public StringClass(){}
}
Using this answer:
var genericObj = new GenericClass<string>();
var stringObj = (StringClass)Convert.ChangeType(genericObj, typeof(StringClass));
Finally i solved using ICloneable,
Here i have a base class named GenericClass, a generic class named GenericClassT, and a class named StringClass.
Inheritance is:
GenericClass <- GenericClassT <- StringClass
Using ICloneable implementation on GenericClass and GenericClassT, adding a CreateObject and CopyTo methods i reach the final goal:
var genericObj = new GenericClass<string>();
var stringObj = (StringClass)genericObj.Clone<StringClass>();
class definitions:
public class GenericClass: ICloneable
{
public string Id {get;set;}
protected virtual ApiRequestResult CreateObject()
{
return new GenericClass();
}
protected virtual void CopyTo(GenericClass obj)
{
obj.Id = Id;
}
public virtual object Clone()
{
var obj = CreateObject();
CopyTo(obj);
return obj;
}
public virtual object Clone<T>() where T: GenericClass
{
var obj = (GenericClass)Activator.CreateInstance(typeof(T));
CopyTo(obj);
return obj;
}
}
public class GenericClass<T>: GenericClass
{
public T Data {get; set;}
protected override GenericClass CreateObject()
{
return new GenericClass<T>();
}
protected override void CopyTo(GenericClass obj)
{
base.CopyTo(obj);
((GenericClass<T>)obj).Data = Data;
}
}
public class StringClass: GenericClass<string>
{
}
Suppose I have two classes and both contain the same fields
Class A
{
public string Name { get; set; }
public int Designaton { get; set; }
}
Class B
{
public string Name { get; set; }
public int Designation { get; set; }
}
And I have one interface and two classes which are inherited from interface
public interface IDeprt
{
object BindData();
}
And two extractor classes:
public classAItem : IDeprt
{
public object BindData()
{
return new A()
{
// mapping operation
}
}
}
public classBItem : IDeprt
{
public object BindData()
{
return new B()
{
//same mapping operation
}
}
}
My question, how can I implement this in generic way using <T> .
Both classes are doing same operation only return type change. If I am doing in the above way there is lot of duplication of code.
Make your ITem interface and also BindData generic make them use the same generic parameter.
public interface IItem<T>
{
T BindData();
}
Then implement the subclasses like below :
public class AItem : ITem<A>
{
public A BindData(){
return new A(){
// mapping operation
}
}
}
public class BItem : ITem<B>
{
public B BindData(){
return new B(){
//same mapping operation
}
}
}
Edit : As the question evolves.
Make a shared base class for A and B classes.
public abstract class CommonItem
{
public string Name { get; set; }
public int Designaton { get; set; }
}
class A : CommonItem
{
}
class B : CommonItem
{
}
Then make class with a method that accepts a generic parameter with new and CommonItem constraints.
public class Binder
{
public T BindData<T>() where T: CommonItem, new()
{
return new T()
{
// you can access the properties defined in ICommonItem
}
}
}
Usage :
var binder = new Binder();
var boundA = binder.BindData<A>();
var boundB = binder.BindData<B>();
My problem is for some reason, I can't use derived class as the base when I pass it through a generic.
Let's suppose the below code to describe the idea
public class Person
{
public virtual bool IsGood { get; }
}
public class GoodPerson : Person
{
public override bool IsGood { get; } = true;
}
public class BadPerson : Person
{
public override bool IsGood { get; } = false;
}
public class Case<T>
{
public T PersonType { get; set; }
}
public class TypeReflector
{
public Person Reflect(Case<Person> person)
{
if (person.PersonType.IsGood)
return (GoodPerson)person.PersonType;
return (BadPerson)person.PersonType;
}
}
and called as below:
var reflector = new TypeReflector();
var result = reflector.Reflect(new Case<GoodPerson>());
why the method Reflect can't be called with Case<GoodPerson>.
but it possible without the Case as below:
public Person Reflect(Person person)
{
if (person.IsGood)
return (GoodPerson)person;
return (BadPerson)person;
}
Since Case<Person> is not the same type as Case<GoodPerson>
Much like..
Case<int> is not the same type like Case<string>
Your reflector expected a strong type Case<Person> but you're providing it a different strong type Case<GoodPerson> (so this is like providing Case<string> to a method which expects Case<int>)
To make it work, make your reflector accept Case where T is a person or derived class of a person like this:
public class TypeReflector
{
public Person Reflect<T>(Case<T> person) where T:Person
{
return person.PersonType;
}
}
But below is what I think you really want to achieve, getting an instance of a type based on properties supplied in descriptor type. A type Person once instantiated cannot be "elevated" to GoodPerson instance without another new keyword somewhere.. (btw, the opposite is possible, creating a GoodPerson and casting to the more basic Person class)
using System;
namespace ConsoleApp25
{
class Program
{
static void Main(string[] args)
{
var personDescriptor = new PersonDescriptor { IsGood = true };
var resultPerson = personDescriptor.CreateInstance();
Console.WriteLine(resultPerson.IsGood);
Console.WriteLine(resultPerson.GetType().Name);
Console.ReadLine();
}
}
public class PersonDescriptor
{
public bool IsGood { get; set; }
public Person CreateInstance()
{
if (IsGood)
return new GoodPerson(); //create new instance!
return new BadPerson(); //create new instance!
}
}
public abstract class Person
{
public abstract bool IsGood { get; }
}
public class GoodPerson : Person
{
public override bool IsGood { get; } = true;
}
public class BadPerson : Person
{
public override bool IsGood { get; } = false;
}
}
Having the following classes:
public class DeviceParameter
{
public string Key { get; set; }
public Guid DeviceId { get; set; }
public string Value { get; set; }
}
A device can have a lot of parameters, of different types, but they are all stored in the database as strings.
public abstract class DeviceValueTypedParameter<TValue>
{
public string CodeName { get; }
public TValue Value { get; set; }
public Guid DeviceId { get; set; }
public DeviceValueTypedParameter(string codeName)
{
this.CodeName = codeName;
}
}
DeviceValueTypedParameter is an abstraction, to have a typed value (TValue) used on C# of the parameter value, instead of using the string that we get from the database. There is no heritance between DeviceValueTypedDeviceParameter and DeviceParameter because I want to make the conversion from TValue to string by composition.
public class ArmingStatusParameter : DeviceValueTypedParameter<ArmingStatuses>
{
public const string CODE_NAME = "ArmingStatus";
public ArmingStatusParameter() : base(CODE_NAME)
{
}
}
public enum ArmingStatuses
{
Unknown,
Armed,
Disarmed,
}
ArmingStatusParameter is an example of a typed Parameter that can exist, where the value is an Enum of ArmingStatuses. Other types that can exist are DateTimes, int32, double, etc.
I've already accomplished the conversion from Typed value to string, but now I'm struggling how to properly do the conversion from string to Typed value.
Tried different approaches:
Implicit or Explicit conversion
Extension method
Converter classes for each type that exists
Generic converter class based on TValue type
Option 1: is easy to implement, but violates the POCO of
ArmingStatusParameter. People can forget to implement the implicit/explicit operators and errors will only happen at compile time.
Option 2: violates the Interface segregation principle (ISP), since is needed to access directly the conversion.
Option 3: it works, but people will have to create a lot of classes and the code will be too verbose. For each different parameter, is needed to instance a new {X}TypedParameterConverter.
Option 4: seems the best option, but I am having troubles "in making it work"
I was thinking about something like this:
public interface IDeviceValueTypedParameterConverter
{
bool TryConvert<T, TValue>(DeviceParameter deviceParameter,
DeviceValueTypedParameter<TValue> deviceValueTypedParameter)
where T : DeviceValueTypedParameter<TValue>;
}
public class DeviceValueTypedParameterConverter : IDeviceValueTypedParameterConverter
{
public bool TryConvert<T, TValue>(DeviceParameter inputParameter,
DeviceValueTypedParameter<TValue> outputParameter)
where T : DeviceValueTypedParameter<TValue>
{
bool result = true;
if (inputParameter == null)
{
throw new NullReferenceException($"DeviceValueTypedParameter:'{typeof(T)}' must be initialized first");
}
if (inputParameter.Value is int)
{
result = int.TryParse(inputParameter.Value, out int temp);
outputParameter.Value = (TValue)temp;
}
else if (inputParameter.Value is Enum)
{
// some other code to convert the Enum's
}
// more else ifs one for each type
// (...)
else
{
result = false;
}
outputParameter.DeviceId = inputParameter.DeviceId;
return result;
}
}
Issues:
All the Ifs gives me a warning saying: "The given expression is never of the provided".
Can't make the cast (TValue). It says can't convert int to TValue. The only solution is creating value via reflection?
Here is my attempt to make this work - I am not sure if it violates some details you did not explain (or did explain). Since out parameters can't use polymorphism, I created an interface to represent the common functions across the typed parameter base class. Since there are no static virtual methods, I used object methods and created a result object that will is used if the conversion is possible.
I see no reason for the conversion method to have multiple instances or need an interface, so I created it as a single static method. I used an enum to capture the type of conversion needed for the parameter accessible from the passed in type, and had to do a tricky conversion through object to handle assignment to the out parameter value field, since C# has no type switching capability for assignments. Note this could cause a runtime error if the IsPossible method doesn't properly filter all cases and the ChangeType fails.
public enum ValueParseTypes {
Enum,
DateTime,
Int
}
public interface IDeviceValueTypedDeviceParameter<TValue> {
string CodeName { get; }
TValue Value { get; set; }
Guid DeviceId { get; set; }
ValueParseTypes ParseType { get; set; }
bool IsPossibleValue(DeviceParameter aValue);
}
public abstract class DeviceValueTypedDeviceParameter<TValue> : IDeviceValueTypedDeviceParameter<TValue> {
public string CodeName { get; }
public TValue Value { get; set; }
public Guid DeviceId { get; set; }
public ValueParseTypes ParseType { get; set; }
public DeviceValueTypedDeviceParameter(string codeName, ValueParseTypes parseType) {
this.CodeName = codeName;
this.ParseType = parseType;
}
public virtual bool IsPossibleValue(DeviceParameter aValue) => false;
}
public class ArmingStatusParameter : DeviceValueTypedDeviceParameter<ArmingStatuses> {
public const string CODE_NAME = "ArmingStatus";
public ArmingStatusParameter() : base(CODE_NAME, ValueParseTypes.Enum) {
}
static HashSet<string> ArmingStatusesNames = Enum.GetNames(typeof(ArmingStatuses)).ToHashSet();
public override bool IsPossibleValue(DeviceParameter aValue) => ArmingStatusesNames.Contains(aValue.Value);
}
public enum ArmingStatuses {
Unknown,
Armed,
Disarmed,
}
public class PoweredOnStatusParameter : DeviceValueTypedDeviceParameter<DateTime> {
public const string CODE_NAME = "PoweredOn";
public PoweredOnStatusParameter() : base(CODE_NAME, ValueParseTypes.DateTime) {
}
public override bool IsPossibleValue(DeviceParameter aValue) => DateTime.TryParse(aValue.Value, out _);
}
public class VoltageStatusParameter : DeviceValueTypedDeviceParameter<int> {
public const string CODE_NAME = "PoweredOn";
public VoltageStatusParameter() : base(CODE_NAME, ValueParseTypes.Int) {
}
public override bool IsPossibleValue(DeviceParameter aValue) => Int32.TryParse(aValue.Value, out _);
}
public static class DeviceValueTypedParameterConverter {
public static bool TryConvert<TValue>(DeviceParameter inputParameter, IDeviceValueTypedDeviceParameter<TValue> outputParameter)
where TValue : struct {
if (inputParameter == null)
throw new ArgumentNullException(nameof(inputParameter));
else if (outputParameter == null)
throw new ArgumentNullException(nameof(outputParameter));
bool result = false;
if (outputParameter.IsPossibleValue(inputParameter)) {
outputParameter.DeviceId = inputParameter.DeviceId;
switch (outputParameter.ParseType) {
case ValueParseTypes.Enum:
if (Enum.TryParse(inputParameter.Value, out TValue typedValue)) {
outputParameter.Value = typedValue;
result = true;
}
break;
case ValueParseTypes.DateTime:
if (DateTime.TryParse(inputParameter.Value, out var dtValue)) {
outputParameter.Value = (TValue)Convert.ChangeType(dtValue, typeof(TValue));
result = true;
}
break;
case ValueParseTypes.Int:
if (Int32.TryParse(inputParameter.Value, out var intValue)) {
outputParameter.Value = (TValue)Convert.ChangeType(intValue, typeof(TValue));
result = true;
}
break;
}
}
return result;
}
}
Now you can use it like so:
var as_tv = new DeviceParameter() {
Key = "testkey",
DeviceId = new Guid(),
Value = "Armed"
};
var asp = new ArmingStatusParameter();
if (DeviceValueTypedParameterConverter.TryConvert<ArmingStatuses>(as_tv, asp)) {
// work with asp
}
var po_tv = new DeviceParameter() {
Key = "testkey2",
DeviceId = new Guid(),
Value = "4/15/2019 17:36"
};
var pop = new PoweredOnStatusParameter();
if (DeviceValueTypedParameterConverter.TryConvert<DateTime>(po_tv, pop)) {
// work with pop
}
var v_tv = new DeviceParameter() {
Key = "testkey3",
DeviceId = new Guid(),
Value = "17"
};
var vp = new VoltageStatusParameter();
if (DeviceValueTypedParameterConverter.TryConvert<int>(v_tv, vp)) {
// work with vp
}
I have 2 collections of 2 different types but have almost the same set of fields.
in one function, I need to iterate through one of the collections depending on one condition.
I want to write only one code block that will cover both cases.
Example:
I have the following code:
if (condition1)
{
foreach(var type1var in Type1Collection)
{
// Do some code here
type1var.Notes = "note";
type1var.Price = 1;
}
}
else
{
foreach(var type2var in Type2Collection)
{
// the same code logic is used here
type2var.Notes = "note";
type2var.Price = 1;
}
}
Now: I want to simplify this code to use the same logic only once ( as they are identical ), something like the following ( P.S : I know the following code is not correct, I am just explaining what I want to do ):
var typecollection = Condition1 ? Type1Collection : Type2Collection;
foreach(var typevar in TypeCollection)
{
// the same code logic is used here
typevar.Notes = "note";
typevar.Price = 1;
}
The definition of Type1 & Type2 is similar to the following code ( Actually they are Entity objects):
public class Type1 : EntityObject
{
public int Type1ID { get; set; }
public int Type1MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
public class Type2 : EntityObject
{
public int Type2ID { get; set; }
public int Type2MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
Update 1:
I have included some sample code I am using inside foreach block ( I am accessing a public properties of the 2 types).
Update 2:
I have included sample Type1 & Type2 definitions, as you can see I have 2 common Public Properties in both classes which I want to update in foreach block.
Update 3:
I am sorry for the confusion, Type1 & Type2 are derived from EntityObject ( They are both part of my entity model, and the Type1Collection & Type2Collection are actually EntityCollection of these 2 entities.
You could use dynamic. Note you will lose type safety.
var list1 = new List<bool>(){true,false};
var list2 = new List<int>(){1,2};
var typecollection = condition1 ? list1.Cast<dynamic>() : list2.Cast<dynamic>();
foreach (var value in typecollection)
{
//then you can call a method you know they both have
Debug.WriteLine(value.ToString());
}
Or if they share a common interface you can cast directly to that. You will maintain type safety
var list1 = new List<bool>(){true,false};
var list2 = new List<int>(){1,2};
var typecollection = condition1 ? list1.Cast<IConvertible>() : list2.Cast<IConvertible>();
foreach (IConvertible convertible in typecollection)
{
//we now know they have a common interface so we can call a common method
Debug.WriteLine(convertible.ToString());
}
Given Jon Skeet's hint of using LINQ's Concat method and the OP's statement that the classes involved are EntityObjects, here's another possible solution. This assumes that the EntityObject subclasses are defined as partial classes:
public partial class Type1 : EntityObject
{
public int Type1ID { get; set; }
public int Type1MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
public partial class Type2 : EntityObject
{
public int Type2ID { get; set; }
public int Type2MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
This allows the OP to declare an interface with the common properties, and have his EntityObject subclasses implement that interface:
public interface IMyType
{
String Notes { get; set; }
decimal Price { get; set; }
}
public partial class Type1 : IMyType {}
public partial class Type2 : IMyType {}
And the original code becomes:
var query = (
from type1var in type1Collection
where condition1
select (IMyType)type1var
).Concat(
from type2var in type2Collection
where !condition1
select (IMyType)type2var
);
foreach(var myType in query)
{
myType.Notes = "note";
myType.Price = 1;
}
You could create a base type for type1 and type2 that groups the common properties between the two classes:
class MyBaseType {
// Common properties
}
class Type1 : MyBaseType {
// Specific properties
}
class Type2 : MyBaseType {
// Specific properties
}
Then, you could do something like this:
IEnumerable<MyBaseType> collection;
if(condition1)
collection = type1Collection;
else
collection = type2Collection;
foreach(MyBaseType element in collection) {
// Common logic
}
EDIT:
As Simon points out in the comments, you should use an interface instead of a base type if it's enough (i.e you don't need a specific implementation for both types).
This is not a very nice way to do it, but it would atleast work.
var type1Collection = new Collection<Type1>();
var type2Collection = new Collection<Type2>();
var condition1 = new Random().Next(0, 2) != 0;
dynamic selectedCollection;
if (condition1)
selectedCollection = type1Collection;
else
selectedCollection = type2Collection;
foreach (var typeVar in selectedCollection)
{
typeVar.Notes = "note";
typeVar.Price = 1;
}
I'm surprised nobody else has suggested an extension method yet:
public interface IMyType
{
String Notes { get; set; }
decimal Price { get; set; }
}
public static class MyTypeExtensions
{
public static void MyLogic(this IMyType myType)
{
// whatever other logic is needed
myType.Notes = "notes";
myType.Price = 1;
}
}
Now, your original types just need to implement IMyType:
public class Type1 : IMyType
{
public int Type1ID { get; set; }
public int Type1MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
public class Type2 : IMyType
{
public int Type2ID { get; set; }
public int Type2MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
Then the original code becomes:
if (condition1)
{
foreach (var type1 in type1Collection)
{
type1.MyLogic();
}
}
else
{
foreach (var type2 in type2Collection)
{
type2.MyLogic();
}
}
You can do it with Predicate and Action stored in a Dictionary. I am suggesting Action here since the code snippet doesn't seems to return anything
public class IterationExample
{
private readonly Dictionary<bool, Action> dictionary;
public IterationExample()
{
dictionary = new Dictionary<bool, Action> { { true, CollectionOneIterator }, { false, CollectionTwoIterator } };
}
public void PublicMethod()
{
dictionary[condition]();
}
private void CollectionOneIterator()
{
foreach (var loopVariable in Type1Collection)
{
//Your code here
}
}
private void CollectionTwoIterator()
{
foreach (var loopVariable in Type2Collection)
{
//Your code here
}
}
}
With this way the readbility and testability of your code improves and also avoids long methods.
Edit:
public class Entity
{
public IList<string> Type1Collection { get; set; }
public IList<string> Type2Collection { get; set; }
}
public class ConsumingClass
{
public void Example()
{
var entity = new Entity();
entity.PublicMethod();
}
}
public static class IterationExample
{
private static readonly Dictionary<bool, Action<Entity>> dictionary;
static IterationExample()
{
dictionary = new Dictionary<bool, Action<Entity>> { { true, CollectionOneIterator }, { false, CollectionTwoIterator } };
}
public static void PublicMethod(this Entity entity)
{
dictionary[condition]();
}
private static void CollectionOneIterator(Entity entity)
{
foreach (var loopVariable in entity.Type1Collection)
{
//Your code here
}
}
private static void CollectionTwoIterator(Entity entity)
{
foreach (var loopVariable in entity.Type2Collection)
{
//Your code here
}
}
}