c# Dispatching Extension Methods based on Value not Reference - c#

Given the following 2 extension methods
public static string getIDPropertyName(this object value)
{
return "ID";
}
public static string getIDPropertyName<IDType>(this EntityRestIdentityDescriber<IDType> entityIdentityDescriber)
{
return entityIdentityDescriber.propertyNameDescribingID();
}
and the following 2 invocations
//arrange
object test = new CustomEntityRestModelIdentity();
//test.UserName = "KKK";
//act
var actual = test.getIDPropertyName(); //calls the first extension method
var actual2 = (test as CustomEntityRestModelIdentity).getIDPropertyName(); //calls the second extension method
How can I execute the second extension method even though its reference type is object but its value type is a EntityRestIdentityDescriber? I'm looking for static polymorphism.

try this
public static string getIDPropertyName(this object entityIdentityDescriber)
{
if(entityIdentityDescriber is EntityRestIdentityDescriber<IDType>)
return entityIdentityDescriber.propertyNameDescribingID();
else
return "id";
}

You need double dispatch.Double dispatch determine dispatch based on the actual type at runtime
public class IdPropertyNameResolver
{
public string GetIDPropertyName(object value)=>"ID";
public string GetIDPropertyName<T>(EntityRestIdentityDescriber<T> value)=>
value.propertyNameDescribingID();
}
//......do something
object o = new CustomEntityRestModelIdentity();
new IdPropertyNameResolver().GetIDPropertyName((dynamic)o);//CustomEntityRestModelIdentity id name
//......

Related

How to stub a function and define equality comparer for a Reference Type parameter

I basically want to stub a function, but define my own equality comparer for a Reference Type parameter.
I want to stub a function to return data. I want the parameter of the method to compare by a particular value instead of a ReferenceEquals. I also do not want to create an equals override for my parameter reference type. I think below is the way to accomplish this but I am receiving an exception. Is there another way to do this and/or what I am doing wrong here?
Exception message:
System.Reflection.AmbiguousMatchException : Ambiguous match found.
public class Parameter
{
public string Property1 { get; set; }
}
public interface IStubbable
{
string DoStuff(Parameter param);
}
public class ThisService
{
private IStubbable _stubbable;
public ThisService(IStubbable stubbable)
{
_stubbable = stubbable;
}
public string DoTheStuff(Parameter param)
{
return _stubbable.DoStuff(param);
}
}
[Test]
public void TestStubbing()
{
const string expectedResult = "Totes";
var iStub = MockRepository.GenerateStub<IStubbable>();
const string prop1 = "cool stub bro";
iStub
.Stub(x => x.DoStuff(Arg<Parameter>.Matches(y => y.Property1 == prop1)))
.Return(expectedResult);
var service = new ThisService(iStub);
var result = service.DoTheStuff(new Parameter() {Property1 = prop1});
Assert.AreEqual(expectedResult, result);
}
This post provided another way of achieving this.
Rhino Mocks - Using Arg.Matches
Change the stub to:
iStub.Stub(x => x.DoStuff(new Parameter()))
.Constraints(new PredicateConstraint<Parameter>(y => y.Property1 == prop1 ))
.Return(expectedResult);

Cannot provide arguments when creating an instance of generic type

I have an object that I want to have read only after it is created... namely because the properties in the constructor must be used in GetHashCode, and therefore can't change once created.
I this is one of many classes that are readonly:
public class AccountLabel
{
private string result;
public AccountLabel(string result)
{
// TODO: Complete member initialization
this.result = result;
}
public string JSONRPCData { get { return this.result; } }
}
I have a generic result class like this
public class JsonResult<T> where T : JObject, new()
{
private T bhash;
private string p;
private JsonErrorResponse error;
private int _id;
private Newtonsoft.Json.Linq.JObject ret;
public JsonResult(Newtonsoft.Json.Linq.JObject ret)
{
this.ret = ret;
var tempError = ret["error"];
var tempid = ret["id"];
JsonErrorResponse error = new JsonErrorResponse(tempError);
this.error = error;
this._id = 1;
var tempresult = ret["result"];
T someResult = new T(tempresult); // <--- here is my problem
}
My problem is that I want to pass an object into T's constructor but can't. When I type this the compiler tells me Cannot provide arguments when creating an instance of variable type
What is the best way to work around this situation?
Should I have an Interface that I can call that will update the property?
Will that previous Interface break encapsulation or allow changes to be made to my object?
How else should I approach this?
You can remove the new type constraint, and use Activator.CreateInstance instead.
Instead of this
T someResult = new T(tempresult);
write this:
T someResult = (T)Activator.CreateInstance(
typeof(T)
, new object[] { tempresult }
);
This is likely to be somewhat slower because of going through reflection, and the static checks by the compiler would not be performed. However, considering your scenario, it does not look like either of these would present a significant problem.
You could pass in a factory delegate to the constructor of JSonResult<T>:
public class JsonResult<T> where T : JObject
{
public JsonResult(Newtonsoft.Json.Linq.JObject ret, Func<object, T> factory)
{
var tempresult = ret["result"];
T someResult = factory(tempresult);
}
}
object in Func<object, T> could be replaced with whatever the type of tempResult actually is.

Is there an easy way to have dynamic/ExpandoObject return strings and not objects?

I want to use a dynamic object where I'd normally use Dictionary<string, string>.
I want to go from
dict.Add("key", "value");
string val = dict["key"];
to
dyna.key = "value";
string val = dyna.key
Typical ExpandoObject will return an object for dyna.key, what's an easy way to make it return strings without casting when accessing each value?
EDIT: I'm sorry, I haven't dug enough into my problem --
It's not that the code I'm calling can't use it as a string, it has THIS:
public static implicit operator SomethingSomething(string name)
That's why it breaks
EDIT2: welp, I'm an idiot, only after recreating it from scratch did I read the full error message: has no applicable method named 'GetMyClass' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
Typical ExpandoObject will return an object for dyna.key
Erm, not exactly, it will return whatever you tell it to return:
dynamic dyna = new ExpandoObject();
dyna.key = "value";
string val = dyna.key;
of course you can no longer rely on compile time checking and the following code will obviously crash at runtime:
dynamic dyna = new ExpandoObject();
dyna.key = "value";
int val = dyna.key;
So make sure that you are using the same type as the one used to store the value.
UPDATE:
class Program
{
static void Main()
{
dynamic dyna = new ExpandoObject();
dyna.key = "value";
Test(dyna.key);
}
public static void Test(string message)
{
Console.WriteLine(message);
}
}
UPDATE 2:
I still don't see what the problem is:
class Program
{
static void Main()
{
dynamic d = new ExpandoObject();
d.key = "value";
Program p = d.key;
Console.WriteLine(p.Name);
}
public string Name { get; set; }
public static implicit operator Program(string name)
{
return new Program
{
Name = name
};
}
}
With dynamic, all casts are implicit, so:
dynamic key = ...
dyna.key = "value";
string val = dyna.key;
already does the job.
More accurately, the member-access (.key) returns dynamic, and this dynamic result has an implicit cast (a type-check, verified at runtime) to the target-type, string.

Store a delegate method as a member of class

I'm doing coded ui testing, basically unit testing for the ui, and I have created a TestObject class that stores a list of assertions to be performed against itself within the TestMethod that instantiates it.
public class TestObject {
public string urlToTest;
public List<Assertion> assertions;
}
public class Assertion {
public List<SearchPropertyExpression> searchPropertyExpressions;
public Action assertMethod;
public string expectedValue; // <-- this works fine if I'll always call a method like AreEqual() where it has an expected value, but what if I want to store a method in assertMethod that has different arguments???
}
public class SearchPropertyExpression {
public string expression;
public string value;
}
I would like to store the assert method (for example: Assert.AreEqaul(object expected, object actual) that I want executed against that particular TestObject and call that later but I'm struggling to get something that is syntactically correct. I'm also struggling with how to pass the arguments for that delegate method (assertMethod) when it's actually called. All methods that I'll be calling are within Microsoft.VisualStudio.TestTools.UnitTesting.Assert. In the example below I would like to call Assert.AreEqaul() but any method with varying arguments could be called. Here's what I've got so far...
[TestMethod]
public void uiTestConnectionsEducationHomePage() {
//instantiate test object
TestObject testObject = new TestObject() {
urlToTest = "/example/home.aspx",
assertions = {
new Assertion() {
searchPropertyExpressions = {
new SearchPropertyExpression() {
expression = HtmlDiv.PropertyNames.Id,
value = "header"
}
},
assertMethod = Assert.AreEqual // <-- this is wrong,I'm thinking I need to tell assertMethod what arguments to expect here, lambda??
}
}
};
// get handle to browser and launch
UiBrowserWindow uiBrowserWindow = new UiBrowserWindow();
uiBrowserWindow.launchUrl(testObject.urlToTest);
// assertions
testObject.assertions.ForEach(x => {
HtmlDiv htmlObject = new HtmlDiv();
x.searchPropertyExpressions.ForEach(p => {
htmlObject = uiBrowserWindow.uiHtmlDocument.searchHtmlElementByAttributeValue<HtmlDiv>(p.expression, p.value);
});
x.assertMethod; // <-- for this is example the arguments would be (htmlObject, "header").
});
}
I think my real problem is that there is a design pattern here that could really help me but I'm not well versed in design patterns.
Your assertMethod delegate is of type Action which represents a method with a return type of void and no parameters, e.g. void Foo().
Assert.AreEqual has many overloads, the most universial being Assert.AreEqual(Object expected, Object actual). I suggest you use this and change your delegate accordingly:
Action<Object, Object> assertMethod;
you can do something like this if you want your delegate to point to any method definition:-
public delegate void MyAction(params object[] args);
public class Assertion
{
public List<PropertyExpression> propertyExpressions;
public MyAction assertMethod;
}
public void Test()
{
var asser = new Assertion()
{
assertMethod = (vals) =>Assert.AreEqual(vals[0],vals[1]);
propertyExpressions = null
};
var asser2 = new Assertion()
{
assertMethod = (vals)=>Assert.AreEqual((string)vals[0],(string)vals[1],(bool)vals[2]);
propertyExpressions = null
};
asser.assertMethod(1, 1);//calling object,object overload
asser2.assertMethod("ab", "cd", true);//calling string,string,bool overload
}

C# Static types cannot be used as parameters

public static void SendEmail(String from, String To, String Subject, String HTML, String AttachmentPath = null, String AttachmentName = null, MediaTypeNames AttachmentType = null)
{
....
// Add an attachment if required
if (AttachmentPath != null)
{
var ct = new ContentType(MediaTypeNames.Text.Plain);
using (var a = new Attachment(AttachmentPath, ct)
{
Name = AttachmentName,
NameEncoding = Encoding.UTF8,
TransferEncoding = TransferEncoding.Base64
})
{
mailMessage.Attachments.Add(a);
}
}
....
}
As you can see the MediaTypeNames AttachmentType throws the error:
'System.Net.Mime.MediaTypeNames': static types cannot be used as parameters
What is the best way to deal with this?
You can't pass a static type to a method as a parameter because then it would have to be instantiated, and you can't create an instance of a static class.
It's not recommended but you can simulate use of Static classes as parameters.
Create an Instance class like this :
public class Instance
{
public Type StaticObject { get; private set; }
public Instance(Type staticType)
{
StaticObject = staticType;
}
public object Call(string name, params object[] parameters)
{
MethodInfo method = StaticObject.GetMethod(name);
return method.Invoke(StaticObject, parameters);
}
public object Call(string name)
{
return Call(name, null);
}
}
Then your function where you would use the static class :
private static void YourFunction(Instance instance)
{
instance.Call("TheNameOfMethodToCall", null);
}
For instance.Call :
The first parameter is the name of the method of your static class to call
The second parameter is the list of arguments to pass to the method.
And use like this :
static void Main(string[] args)
{
YourFunction(new Instance(typeof(YourStaticClass)));
Console.ReadKey();
}
The best deal is definitely to remove the last parameter. Since type is static you don't need a reference to an instance and you can refer to its members from your function body.
Use a different type for the argument.
A method argument needs to be of a type that can accept a reference to an instance, so it can't be a static class.
You can wrap static types around an interface or another non-static class and add that as the parameter. Not ideal but a way around it. Or simply just reference the static type in the method body itself.
Send a static class as the type of the parameter
and then give it a variable name for use in the function.
This works because the new variable is a reference to the static class.
It is necessary to address the global variable problem.
If you use a static class as a variable inside a method,
you need to pass it in as a parameter, to avoid the global variable issue.
This is basic structured programming 101 from the 80's.
It doesn't look like you even use that parameter in your method. You should just remove it because MediaTypeNames cannot be instantiated anyway.
A workaround to passing static parameters is to pass it as an object.
Function:
public static void HoldKey(object key)
{
...
}
Call function:
Function(static param);

Categories