StructureMap default instance and additional with explicit arguments - c#

I've got an interface and a class that implements that interface. That class has a default static instance and can also be constructed explicitly (passing an argument).
How do i configure StrucutreMap so that default instance is the static instance, and if i request an instance with an argument a new one is constructed?
Here's the test that fails
[TestFixture]
public class StructureMapTests
{
[Test]
public void Test_same_interface_default_implemenation_and_with_parameter()
{
IMyInterface defaultImp = new MyImpl(0);
ObjectFactory.Initialize(x =>
{
x.For<IMyInterface>().Use(defaultImp);
x.For<IMyInterface>().Add<MyImpl>().Ctor<int>().IsTheDefault();
});
Assert.AreEqual(defaultImp, ObjectFactory.GetInstance<IMyInterface>());
var instance1 = ObjectFactory.With("value").EqualTo(1).GetInstance<IMyInterface>();
Assert.AreEqual(true, instance1 != defaultImp); //<-- fails here
Assert.AreEqual(1, instance1.GetValue());
var instance2 = ObjectFactory.With("value").EqualTo(2).GetInstance<IMyInterface>();
Assert.AreEqual(true, instance1 != defaultImp);
Assert.AreEqual(2, instance2.GetValue());
}
public interface IMyInterface
{
int GetValue();
}
public class MyImpl : IMyInterface
{
private int _value;
public MyImpl(int value)
{
_value = value;
}
public int GetValue()
{
return _value;
}
}
}

I think that the problem that you are facing is that when registering multiple implementations for the same interface, the last one is the one that is going to be resolved by GetInstance. To solve this, you could name your configurations.
Try the following:
[TestFixture]
public class StructureMapTests
{
[Test]
public void Test_same_interface_default_implemenation_and_with_parameter()
{
IMyInterface defaultImp = new MyImpl(0);
ObjectFactory.Initialize(x =>
{
x.For<IMyInterface>().Add<MyInterface>().Named("withArgument").Ctor<int>().IsTheDefault();
x.For<IMyInterface>().Use(defaultImp).Named("default");
});
Assert.AreEqual(defaultImp, ObjectFactory.GetInstance<IMyInterface>());
var instance1 = ObjectFactory.With("value").EqualTo(1).GetInstance<IMyInterface>("withArgument");
Assert.AreEqual(true, instance1 is MyInterface);
Assert.AreEqual(1, instance1.GetValue());
var instance2 = ObjectFactory.With("value").EqualTo(2).GetInstance<IMyInterface>("withArgument");
Assert.AreEqual(true, instance2 is MyInterface);
Assert.AreEqual(2, instance2.GetValue());
}
public interface IMyInterface
{
int GetValue();
}
private class MyInterface : IMyInterface
{
private int _value;
public MyInterface(int value)
{
_value = value;
}
public int GetValue()
{
return _value;
}
}
}

Related

LightInject typed factories with parameters

Per documentation of LightInject we can create a typed factory and pass a value to it like this:
public class Foo : IFoo
{
public Foo(int value)
{
Value = value;
}
public int Value { get; private set; }
}
public interface IFooFactory
{
IFoo GetFoo(int value);
}
public class FooFactory : IFooFactory
{
private Func<int, IFoo> createFoo;
public FooFactory(Func<int, IFoo> createFoo)
{
this.createFoo = createFoo;
}
public IFoo GetFoo(int value)
{
return createFoo(value);
}
}
And we register it like this:
container.Register<int, IFoo>((factory, value) => new Foo(value));
container.Register<IFooFactory, FooFactory>(new PerContainerLifetime());
And we can call GetFoo like this:
var typedFooFactory = container.GetInstance<IFooFactory>();
var foo = typedFooFactory.GetFoo(42);
So now my question is: we instantiate foo explicitly when we are registering the factory new Foo(value). But what if we want to do it within our GetFoo method based on the value?
public IFoo GetFoo(int value)
{
switch (value)
{
case 1: return new Foo(1);
case 2: return new FooThatImplementsIFoo(2);
case 3: return new AnotherFooThatImplementsIFoo(3);
default: return null;
}
}
I want to be able to call GetFoo and get the proper implementation based on "value".
var typedFooFactory = container.GetInstance<IFooFactory>();
var foo = typedFooFactory.GetFoo(3);
Assert.AreEqual(foo.GetType(),typeof(AnotherFooThatImplementsIFoo));
The key is to remember that within GetFoo method of our factory, we are not invoking the delegate, but only passing around the reference to it. The invocation is when we are registering the factory (by instantiating a Foo object).
So what I did was to create a static Factory class that generates implementations of IFoo based on "value":
container.Register<int, IFoo>((factory, value) => FooFactory.GetInstance);
And my FooFactory looks like this:
public static class FooFactory
{
public static IFoo GetInstance(IServiceFactory serviceFactory, int value)
{
switch (value)
{
case 1:
{
return serviceFactory.GetInstance<Foo>();
}
case 2:
{
return serviceFactory.GetInstance<FooThatImplementsIFoo>();
}
case 3:
{
return serviceFactory.GetInstance<AnotherFooThatImplementsIFoo>();
}
default:
{
return null;
}
}
}
}
So I invoke the delegate by doing this:
var typedFooFactory = container.GetInstance<IFooFactory>();
var foo = typedFooFactory.GetFoo(3);
And now my foo object is of Type AnotherFooThatImplementsIFoo
Assert.AreEqual(foo.GetType(),typeof(AnotherFooThatImplementsIFoo));

How to write a generic method and its default equivalent in C#

I think this example is extremely simple and shows what I am trying to achieve:
public class A : A<object>
{
// public static A GetInstance() => new A();
// I can't do this, because cannot override static methods
}
public class A<T>
{
public static A<T> GetInstance() => new A<T>();
// public static A GetInstance() => new A();
// can't do this, because it says that the method is already defined
}
I basically want to be able to use the method GetInstance in two ways:
Specifying the type T: A<T>.GetInstance() or A.GetInstance<T>() (doesn't matter which one) - which should return the type A<T>
Without specifying this type: A.GetInstance() - which should return the type A.
How can I achieve this?
using System;
public class Program
{
public static void Main()
{
Console.WriteLine(A.GetInstance());
Console.WriteLine(A<int>.GetInstance());
Console.WriteLine(A.GetInstance<bool>());
/*
output :
Program+A
Program+A`1[System.Int32]
Program+A`1[System.Boolean]
*/
}
// classes
public class A : A<object>
{
// non-generic method in non-generic class
public static A GetInstance()
{
return new A();
}
// generic method
public static A<T> GetInstance<T>()
{
return new A<T>();
}
}
public class A<T>
{
// non-generic method in generic class
public static A<T> GetInstance()
{
return new A<T>();
}
}
}
public class A<T>
{
public static A<T> GetInstance()
{
return new A<T>();
}
}
public class A : A<object>
{
public static new A GetInstance()
{
return new A();
}
public static A<T> GetInstance<T>()
{
return new A<T>();
}
}
Notice the new keyword to the left of the A GetInstance().
Looks like this is what you want:
public class A : A<object>
{
public static A<T> GetInstance<T>() => new A<T>();
public new static A GetInstance() => new A();
}
public class A<T>
{
public static A<T> GetInstance() => new A<T>();
}
Then use it like this:
var a = A<int>.GetInstance();
var b = A.GetInstance<int>();
var c = A.GetInstance();
However, normally you'd expect to see generic types inherit from their non-generic equivalent, not the other way round (e.g. IEnumerable<T> : IEnumerable)
How about just making a factory like :
public class Factory<T>
{
public static A<T> Instance { get {return new A<T>(); } }
}
void Main()
{
var a = A.GetInstance();
var b = A<object>.GetInstance();
Console.WriteLine(a);
Console.WriteLine(b);
}
class A<T>
{
public static A<T> GetInstance()
{
return new A<T>();
}
}
class A : A<object>
{
}

AutoFixture.AutoMoq supply a known value for one constructor parameter

I've just started to use AutoFixture.AutoMoq in my unit tests and I'm finding it very helpful for creating objects where I don't care about the specific value. After all, anonymous object creation is what it is all about.
What I'm struggling with is when I care about one or more of the constructor parameters. Take ExampleComponent below:
public class ExampleComponent
{
public ExampleComponent(IService service, string someValue)
{
}
}
I want to write a test where I supply a specific value for someValue but leave IService to be created automatically by AutoFixture.AutoMoq.
I know how to use Freeze on my IFixture to keep hold of a known value that will be injected into a component but I can't quite see how to supply a known value of my own.
Here is what I would ideally like to do:
[TestMethod]
public void Create_ExampleComponent_With_Known_SomeValue()
{
// create a fixture that supports automocking
IFixture fixture = new Fixture().Customize(new AutoMoqCustomization());
// supply a known value for someValue (this method doesn't exist)
string knownValue = fixture.Freeze<string>("My known value");
// create an ExampleComponent with my known value injected
// but without bothering about the IService parameter
ExampleComponent component = this.fixture.Create<ExampleComponent>();
// exercise component knowning it has my known value injected
...
}
I know I could do this by calling the constructor directly but this would no longer be anonymous object creation. Is there a way to use AutoFixture.AutoMock like this or do I need to incorporate a DI container into my tests to be able to do what I want?
EDIT:
I probably should have been less absract in my original question so here is my specific scenario.
I have an ICache interface which has generic TryRead<T> and Write<T> methods:
public interface ICache
{
bool TryRead<T>(string key, out T value);
void Write<T>(string key, T value);
// other methods not shown...
}
I'm implementing a CookieCache where ITypeConverter handles converting objects to and from strings and lifespan is used to set the expiry date of a cookie.
public class CookieCache : ICache
{
public CookieCache(ITypeConverter converter, TimeSpan lifespan)
{
// usual storing of parameters
}
public bool TryRead<T>(string key, out T result)
{
// read the cookie value as string and convert it to the target type
}
public void Write<T>(string key, T value)
{
// write the value to a cookie, converted to a string
// set the expiry date of the cookie using the lifespan
}
// other methods not shown...
}
So when writing a test for the expiry date of a cookie, I care about the lifespan but not so much about the converter.
So I'm sure people could work out the generalized implementation of Mark's suggestion but I thought I'd post it for comments.
I've created a generic ParameterNameSpecimenBuilder based on Mark's LifeSpanArg:
public class ParameterNameSpecimenBuilder<T> : ISpecimenBuilder
{
private readonly string name;
private readonly T value;
public ParameterNameSpecimenBuilder(string name, T value)
{
// we don't want a null name but we might want a null value
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException("name");
}
this.name = name;
this.value = value;
}
public object Create(object request, ISpecimenContext context)
{
var pi = request as ParameterInfo;
if (pi == null)
{
return new NoSpecimen(request);
}
if (pi.ParameterType != typeof(T) ||
!string.Equals(
pi.Name,
this.name,
StringComparison.CurrentCultureIgnoreCase))
{
return new NoSpecimen(request);
}
return this.value;
}
}
I've then defined a generic FreezeByName extension method on IFixture which sets the customization:
public static class FreezeByNameExtension
{
public static void FreezeByName<T>(this IFixture fixture, string name, T value)
{
fixture.Customizations.Add(new ParameterNameSpecimenBuilder<T>(name, value));
}
}
The following test will now pass:
[TestMethod]
public void FreezeByName_Sets_Value1_And_Value2_Independently()
{
//// Arrange
IFixture arrangeFixture = new Fixture();
string myValue1 = arrangeFixture.Create<string>();
string myValue2 = arrangeFixture.Create<string>();
IFixture sutFixture = new Fixture();
sutFixture.FreezeByName("value1", myValue1);
sutFixture.FreezeByName("value2", myValue2);
//// Act
TestClass<string> result = sutFixture.Create<TestClass<string>>();
//// Assert
Assert.AreEqual(myValue1, result.Value1);
Assert.AreEqual(myValue2, result.Value2);
}
public class TestClass<T>
{
public TestClass(T value1, T value2)
{
this.Value1 = value1;
this.Value2 = value2;
}
public T Value1 { get; private set; }
public T Value2 { get; private set; }
}
You have to replace:
string knownValue = fixture.Freeze<string>("My known value");
with:
fixture.Inject("My known value");
You can read more about Inject here.
Actually the Freeze extension method does:
var value = fixture.Create<T>();
fixture.Inject(value);
return value;
Which means that the overload you used in the test actually called Create<T> with a seed: My known value resulting in "My known value4d41f94f-1fc9-4115-9f29-e50bc2b4ba5e".
You could do something like this. Imagine that you want to assign a particular value to the TimeSpan argument called lifespan.
public class LifespanArg : ISpecimenBuilder
{
private readonly TimeSpan lifespan;
public LifespanArg(TimeSpan lifespan)
{
this.lifespan = lifespan;
}
public object Create(object request, ISpecimenContext context)
{
var pi = request as ParameterInfo;
if (pi == null)
return new NoSpecimen(request);
if (pi.ParameterType != typeof(TimeSpan) ||
pi.Name != "lifespan")
return new NoSpecimen(request);
return this.lifespan;
}
}
Imperatively, it could be used like this:
var fixture = new Fixture();
fixture.Customizations.Add(new LifespanArg(mySpecialLifespanValue));
var sut = fixture.Create<CookieCache>();
This approach can be generalized to some degree, but in the end, we're limited by the lack of a strongly typed way to extract a ParameterInfo from a particular constructor or method argument.
I fee like #Nick was almost there. When overriding the constructor argument, it needs to be for the given type and have it limited to that type only.
First we create a new ISpecimenBuilder that looks at the "Member.DeclaringType" to keep the correct scope.
public class ConstructorArgumentRelay<TTarget,TValueType> : ISpecimenBuilder
{
private readonly string _paramName;
private readonly TValueType _value;
public ConstructorArgumentRelay(string ParamName, TValueType value)
{
_paramName = ParamName;
_value = value;
}
public object Create(object request, ISpecimenContext context)
{
if (context == null)
throw new ArgumentNullException("context");
ParameterInfo parameter = request as ParameterInfo;
if (parameter == null)
return (object)new NoSpecimen(request);
if (parameter.Member.DeclaringType != typeof(TTarget) ||
parameter.Member.MemberType != MemberTypes.Constructor ||
parameter.ParameterType != typeof(TValueType) ||
parameter.Name != _paramName)
return (object)new NoSpecimen(request);
return _value;
}
}
Next we create an extension method to allow us to easily wire it up with AutoFixture.
public static class AutoFixtureExtensions
{
public static IFixture ConstructorArgumentFor<TTargetType, TValueType>(
this IFixture fixture,
string paramName,
TValueType value)
{
fixture.Customizations.Add(
new ConstructorArgumentRelay<TTargetType, TValueType>(paramName, value)
);
return fixture;
}
}
Now we create two similar classes to test with.
public class TestClass<T>
{
public TestClass(T value1, T value2)
{
Value1 = value1;
Value2 = value2;
}
public T Value1 { get; private set; }
public T Value2 { get; private set; }
}
public class SimilarClass<T>
{
public SimilarClass(T value1, T value2)
{
Value1 = value1;
Value2 = value2;
}
public T Value1 { get; private set; }
public T Value2 { get; private set; }
}
Finally we test it with an extension of the original test to see that it will not override similarly named and typed constructor arguments.
[TestFixture]
public class AutoFixtureTests
{
[Test]
public void Can_Create_Class_With_Specific_Parameter_Value()
{
string wanted = "This is the first string";
string wanted2 = "This is the second string";
Fixture fixture = new Fixture();
fixture.ConstructorArgumentFor<TestClass<string>, string>("value1", wanted)
.ConstructorArgumentFor<TestClass<string>, string>("value2", wanted2);
TestClass<string> t = fixture.Create<TestClass<string>>();
SimilarClass<string> s = fixture.Create<SimilarClass<string>>();
Assert.AreEqual(wanted,t.Value1);
Assert.AreEqual(wanted2,t.Value2);
Assert.AreNotEqual(wanted,s.Value1);
Assert.AreNotEqual(wanted2,s.Value2);
}
}
This seems to be the most comprehensive solution set here. So I'm going to add mine:
The first thing to create ISpecimenBuilder that can handle multiple constructor parameters
internal sealed class CustomConstructorBuilder<T> : ISpecimenBuilder
{
private readonly Dictionary<string, object> _ctorParameters = new Dictionary<string, object>();
public object Create(object request, ISpecimenContext context)
{
var type = typeof (T);
var sr = request as SeededRequest;
if (sr == null || !sr.Request.Equals(type))
{
return new NoSpecimen(request);
}
var ctor = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault();
if (ctor == null)
{
return new NoSpecimen(request);
}
var values = new List<object>();
foreach (var parameter in ctor.GetParameters())
{
if (_ctorParameters.ContainsKey(parameter.Name))
{
values.Add(_ctorParameters[parameter.Name]);
}
else
{
values.Add(context.Resolve(parameter.ParameterType));
}
}
return ctor.Invoke(BindingFlags.CreateInstance, null, values.ToArray(), CultureInfo.InvariantCulture);
}
public void Addparameter(string paramName, object val)
{
_ctorParameters.Add(paramName, val);
}
}
Then create extension method that simplifies usage of created builder
public static class AutoFixtureExtensions
{
public static void FreezeActivator<T>(this IFixture fixture, object parameters)
{
var builder = new CustomConstructorBuilder<T>();
foreach (var prop in parameters.GetType().GetProperties())
{
builder.Addparameter(prop.Name, prop.GetValue(parameters));
}
fixture.Customize<T>(x => builder);
}
}
And usage:
var f = new Fixture();
f.FreezeActivator<UserInfo>(new { privateId = 15, parentId = (long?)33 });
Good thread, I added another twist based on many of the aswers already posted:
Usage
Example:
var sut = new Fixture()
.For<AClass>()
.Set("value1").To(aInterface)
.Set("value2").ToEnumerableOf(22, 33)
.Create();
Test classes:
public class AClass
{
public AInterface Value1 { get; private set; }
public IEnumerable<int> Value2 { get; private set; }
public AClass(AInterface value1, IEnumerable<int> value2)
{
Value1 = value1;
Value2 = value2;
}
}
public interface AInterface
{
}
Full test
public class ATest
{
[Theory, AutoNSubstituteData]
public void ATestMethod(AInterface aInterface)
{
var sut = new Fixture()
.For<AClass>()
.Set("value1").To(aInterface)
.Set("value2").ToEnumerableOf(22, 33)
.Create();
Assert.True(ReferenceEquals(aInterface, sut.Value1));
Assert.Equal(2, sut.Value2.Count());
Assert.Equal(22, sut.Value2.ElementAt(0));
Assert.Equal(33, sut.Value2.ElementAt(1));
}
}
Infrastructure
Extension method:
public static class AutoFixtureExtensions
{
public static SetCreateProvider<TTypeToConstruct> For<TTypeToConstruct>(this IFixture fixture)
{
return new SetCreateProvider<TTypeToConstruct>(fixture);
}
}
Classes participating in the fluent style:
public class SetCreateProvider<TTypeToConstruct>
{
private readonly IFixture _fixture;
public SetCreateProvider(IFixture fixture)
{
_fixture = fixture;
}
public SetProvider<TTypeToConstruct> Set(string parameterName)
{
return new SetProvider<TTypeToConstruct>(this, parameterName);
}
public TTypeToConstruct Create()
{
var instance = _fixture.Create<TTypeToConstruct>();
return instance;
}
internal void AddConstructorParameter<TTypeOfParam>(ConstructorParameterRelay<TTypeToConstruct, TTypeOfParam> constructorParameter)
{
_fixture.Customizations.Add(constructorParameter);
}
}
public class SetProvider<TTypeToConstruct>
{
private readonly string _parameterName;
private readonly SetCreateProvider<TTypeToConstruct> _father;
public SetProvider(SetCreateProvider<TTypeToConstruct> father, string parameterName)
{
_parameterName = parameterName;
_father = father;
}
public SetCreateProvider<TTypeToConstruct> To<TTypeOfParam>(TTypeOfParam parameterValue)
{
var constructorParameter = new ConstructorParameterRelay<TTypeToConstruct, TTypeOfParam>(_parameterName, parameterValue);
_father.AddConstructorParameter(constructorParameter);
return _father;
}
public SetCreateProvider<TTypeToConstruct> ToEnumerableOf<TTypeOfParam>(params TTypeOfParam[] parametersValues)
{
IEnumerable<TTypeOfParam> actualParamValue = parametersValues;
var constructorParameter = new ConstructorParameterRelay<TTypeToConstruct, IEnumerable<TTypeOfParam>>(_parameterName, actualParamValue);
_father.AddConstructorParameter(constructorParameter);
return _father;
}
}
Constructor parameter relay from other answers:
public class ConstructorParameterRelay<TTypeToConstruct, TValueType> : ISpecimenBuilder
{
private readonly string _paramName;
private readonly TValueType _paramValue;
public ConstructorParameterRelay(string paramName, TValueType paramValue)
{
_paramName = paramName;
_paramValue = paramValue;
}
public object Create(object request, ISpecimenContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
ParameterInfo parameter = request as ParameterInfo;
if (parameter == null)
return new NoSpecimen();
if (parameter.Member.DeclaringType != typeof(TTypeToConstruct) ||
parameter.Member.MemberType != MemberTypes.Constructor ||
parameter.ParameterType != typeof(TValueType) ||
parameter.Name != _paramName)
return new NoSpecimen();
return _paramValue;
}
}

Using params in a Delegate as a base class

I have a delegate expecting parameters of type A as parameters. So A is the base class. Class B and C inherit from A.
The problem is that although B and C inherit from the base class A, the DoSomething functions at the bottom of the script can't be converted to the delegate.
public class A { }
public class B : A { }
public class C : A { }
public delegate void CallbackAction(params A[] paremeters);
public class Main
{
public int main(params string[] args)
{
CallbackAction callbackAction;
callbackAction = DoSomething1;
callbackAction = DoSomething2;
callbackAction = DoSomething3;
return 0;
}
public void DoSomething1(A arg0) { }
public void DoSomething2(B arg0) { }
public void DoSomething3(C arg0) { }
}
Is there any way to use params in a delegate and be able to use classes that have the params class as their base class?
When compiling the error I get is:
Error 5 No overload for 'DoSomething3' matches delegate 'SKConsole.CallbackAction'
I'm using .NET 4 and XNA
EDIT::
Ok let me explain why I am using this I am creating a console. This means a programmer using my console can add a command (console.AddCommand("help", Help) to the console, Help here is a function. When you are ingame and typing help in the console it will execute the function Help().
I now want it to work with console.AddCommand("setSpeed", SetPlayerSpeed) aswell. The SetPlayerSpeed function has 1 parameter, an int. But I want it to work with any function so if a programmer creates the function DoSomeFancyStuff(float a, string b, int c) I want the console to create a command and if you type in the correct string in the console execute these command.
I already tried making lots of delegates for different functions, but this is kinda ugly in my opinion.
What I then tried was the following
public abstract class SKConsoleParameter
{
protected string value;
public SKConsoleParameter(string value)
{
this.value = value;
}
public string GetRawValue()
{
return value;
}
public abstract bool IsValid();
public abstract object GetValue();
}
public class StringParam : SKConsoleParameter
{
public StringParam(string value) : base(value) { }
public override bool IsValid()
{
return true;
}
public override object GetValue()
{
return value;
}
}
public class IntParam : SKConsoleParameter
{
public IntParam(string value) : base(value) { }
public override bool IsValid()
{
int i;
return int.TryParse(value, out i);
}
public override object GetValue()
{
int i;
if (int.TryParse(value, out i))
return i;
else
return 0;
}
}
Was this does is that if a developer creates a function like:
DoSomethingCool(StringParam s, IntParam i)
Then it can receive the values by using (string)s.GetValue() and (int)i.GetValue()
The StringParam and IntParam classes both inherit from SKConsoleParameter, so i though I could now create the follwoing delegate
void CoolDelegate(params SKConsoleParameter[] parameters)
But this doesn't work.. Because of the abstract problem with class A, B and C at the top of this page
Does anyone have any ideas to counter this problem?
You are looking for the error in the wrong place. C# allows contravariance in delegates' input parameters. The problem with your code is that your delegate takes params A[], while your method takes a single A. This is not allowed. Delcare your delegate as accepting a single A:
delegate void Callback1 (B a) ;
void Test11 (A a) {}
void Test12 (B b) {}
Callback1 c11 = Test11 ; // OK
Callback1 c12 = Test12 ; // OK
Note also that this does not work with array parameters:
delegate void Callback2 (B[] a) ;
void Test21 (A[] a) {}
void Test22 (B[] b) {}
Callback2 c21 = Test21 ; // compile error
Callback2 c22 = Test22 ; // OK
based on your updated code -- try this. As long as your method matches the delegate the contravariance should work as expected in .NET 3.5 or better
public abstract class SKConsoleParameter
{
protected string value;
public SKConsoleParameter(string value)
{
this.value = value;
}
public string GetRawValue()
{
return value;
}
public abstract bool IsValid();
public abstract object GetValue();
}
public class StringParam : SKConsoleParameter
{
public StringParam(string value) : base(value) { }
public override bool IsValid()
{
return true;
}
public override object GetValue()
{
return value;
}
}
public class IntParam : SKConsoleParameter
{
public IntParam(string value) : base(value) { }
public override bool IsValid()
{
int i;
return int.TryParse(value, out i);
}
public override object GetValue()
{
int i;
if (int.TryParse(value, out i))
return i;
else
return 0;
}
}
class Program
{
public delegate void CoolDelegate(params SKConsoleParameter[] parameters);
static void Main(string[] args)
{
var s = new StringParam("Glenn");
var i = new IntParam("12");
var coolDel = new CoolDelegate(DoSomethingCool);
coolDel(s, i);
}
public static void DoSomethingCool(params SKConsoleParameter[] parameters)
{
if (parameters == null) throw new ArgumentNullException("parameters");
foreach (var item in parameters)
{
if (item is IntParam)
{
// do something interesting
continue;
}
if (item is StringParam)
{
// do something else interesting
continue;
}
throw new NotImplementedException("unknown param type");
}
}
}
Try this code:
public class A { }
public class B : A { }
public class C : A { }
public static class Helper
{
public static Action<A> DoSomething;
}
class Program
{
static void Main(string[] args)
{
var a = new A();
var b = new B();
var c = new C();
Helper.DoSomething = new Action<A>(DoSomething1);
Helper.DoSomething = (Action<A>)new Action<B>(DoSomething2);
Helper.DoSomething = (Action<A>)new Action<C>(DoSomething3);
}
public static void DoSomething1(A a) { }
public static void DoSomething2(B a) { }
public static void DoSomething3(C a) { }
}

Finding out type of a generic class

Lets say I have a generic class:
class Foo {
// protected Type t;
// public void SetT(string strval) {
// ((Foo<t>)this).Set(strval);
// }
}
class Foo<T> : Foo {
private T val;
public void Set(string strval) {
if (this is Foo<float>) {
this.val = float.Parse(strval);
} else if (this is Foo<int>) {
this.val = int.Parse(strval);
}
}
}
Now I create an object and put it in an ArrayList:
ArrayList a = new ArrayList();
a.Append(new Foo<float>);
And then I forget the type of Foo<>. Now, how do I Set? I tried the obvious candidates:
(Foo)a[0].Set("5.0");
(Foo<a[0].GetType()>)a[0].Set("5.0");
but those failed.
Is there a way I can call that Set method without explicitly knowing the type of Foo<>?
If not, can I somehow save type of Foo into Foo.t, and then uncomment and use Foo.SetT?
Ah, generics. Very nice tool if you know how to use them :-)
Regards,
dijxtra
One way is to make your generic Foo class implement an interface:
interface IFoo {
void Set(string strval);
}
class Foo<T> : IFoo {
private T val;
public void Set(string strval) {
...
}
}
Then you can cast to IFoo and call Set():
((IFoo)a[0]).Set("5.0");
There's absolutely no reason to be using generics here. Generics are intended to be used when the type of operations you will be performing are generic. In other words, they are independent of the type(s) on which they are performed. You are doing the opposite: the operation will be different depending on the types.
Given that, you should remove the generic parameter, make Set() and Foo abstract, and derive appropriate classes to handle the different types:
abstract class Foo
{
public abstract void Set(string value);
}
class FooDouble : Foo
{
double val;
public override void Set(string value)
{
this.val = double.Parse(value);
}
}
// Etc.
Then, you should be storing your Foos in a List<T>:
List<Foo> fooList = new List<Foo>();
fooList.Add(new FooDouble());
Later, you can say this:
fooList[0].Set("5.0");
And it will just work! No need to remember!
You want to override the implementation of Set in the derived classes.
class Foo {
public virtual void Set(string val);
}
class Foo<T> : Foo {
public override void Set(string val);
}
In addition to what Jimmy pointed out for your base class, you could use a generic collection instead of an ArrayList and make use of a type converter:
public interface IFoo
{
void Set(string value);
}
public class Foo<T> : IFoo
{
private T val;
public void Set(string value)
{
var typeConverter = TypeDescriptor.GetConverter(typeof(T));
if(typeConverter.CanConvertFrom(typeof(string)))
{
val = (T)typeConverter.ConvertFromString(value);
}
else
{
throw new InvalidOperationException();
}
}
}
The above will work with either your ArrayList:
ArrayList a = new ArrayList();
a.Append(new Foo<float>());
((IFoo)a[0]).Set("123.4");
Or with a typed collection:
List<IFoo> list = new List<IFoo>();
list.Add(new Foo<float>());
list[0].Set("123.4");
As an added bonus, you don't need to have an if statement in your Set method and try to account for all possible types.
If you want to know the type parameter that was used in you generic, use the GetGenericArguments method.
class Foo<T> {
int input_as_int;
float input_as_float;
public void Set(string strval) {
if (this.GetType().GetGenericArguments().First() == typeof(float)) {
this.input_as_float = float.Parse(strval);
} else if (this.GetType().GetGenericArguments().First() == typeof(int)) {
this.input_as_int = int.Parse(strval);
}
// Else .. throw an exception? return default value? return 0? what makes sense to your application
}
or alternately if you could by pass the Interface entirely and pass the input string in the constructor.
public class Foo<T>
{
public Foo (string input)
{
var typeConverter = TypeDescriptor.GetConverter(typeof(T));
if (typeConverter.CanConvertFrom(typeof(string)))
{
Value = (T)typeConverter.ConvertFromString(input);
}
else
{
throw new InvalidOperationException();
}
}
public T Value { get; set;
}
}
then you can just use it like so.
var test = new List<int> Foo ("3");
using System;
using System.Collections;
using System.Collections.Generic;
class Foo {
}
class Foo<T> : Foo {
private T val;
public void Set(string strval) {
var _type = typeof(T);
val = (T)(_type.InvokeMember("Parse", System.Reflection.BindingFlags.InvokeMethod, null, null, new Object[] { strval }));
}
override public string ToString(){
return String.Format("{0}", val);
}
}
class Sample {
static void Main(string[] args){
ArrayList a = new ArrayList();
a.Add(new Foo<float>());
a.Add(new Foo<int>());
dynamic ax = a[0];
ax.Set("5.5");
ax = a[1];
ax.Set("55");
//EDIT
//But I may have to set the float value to Foo <int> If you forgot
// ((Foo<float>)a[0]).Set("5.5");
// ((Foo<int>)a[1]).Set("55");
Console.WriteLine("{0},{1}", a[0], a[1]);
}
}

Categories