Invoke Method of instance class through string - c#

this link: http://www.codeproject.com/Articles/19911/Dynamically-Invoke-A-Method-Given-Strings-with-Met
explains clearly how to invoke a method when you have a method name + type as string variable.
I'm making a c# project with WatiN. All the WatiN methods I use are of the same form:
Examples: *(ById,.ByClass..; so should be connected, but then I could not set it bold :s )
browser.**TextField**(Find.By **Id**("name")).**TypeText**("my name");
browser.**Span**(Find.By **Class**("main_title")).**Click(**);
browser.**Link**(Find.By **Id**("mid_link")).**Click(**);
As you can see, this always consist of 3 methods which are variable. I created a class webElement consisted of the string properties: tag, type, search, action.
Where in example -> tag = "TextField"; type = "Id", search = "name"; action = "TypeText".
To get the web-elements dynamically, I created a WebHandler class where I try to dynamically call the right methods.
So a main class has a list of all the webElement objects and can give the right one at a given time to the WebHandler class. The Webhandler class should now invoke each element dynamically. I use the same invoke method as in given url, so my code to call it is:
class WebHandler:
private IE browser = new IE("google.com");
public void method(WebElement webElement)
{
//Get the findBy dynamically | this works
WatiN.Core.Constraints.Constraint findBy =
(WatiN.Core.Constraints.Constraint)InvokeMethod("WatiN.Core.Find, WatiN.Core", "By" + webElement.Type, webElement.Search); //where type = "Id" and search = "name"
//Get the tag (like textfield, link, span) dynamically | this does not work
Type aType = Type.GetType("WatiN.Core." + webElement.Tag, "WatiN.Core") //how can I set the element variable to this type? aType element -> Does not work
aType element = (WatiN.Core.TextField)InvokeMethod("this.browser", webElement.Tag, findBy); //tag = TextField
element.TypeText("a name"); //same problem as above | so should be invoked
}
QUESTIONS:
How do I invoke method (TextField) of instance class IE
(browser) dynamically using his string version "TextField" as variable? Another way of phrasing it would be: How do I get current variable (browser) by using it's string version "browser"?
How do I set type of variable element dynamically? So when webElement.Tag = Textfield then type should be WatiN.Core.TexField
element = .. (see code)
OWN CONSIDERATIONS:
Main problem I found is that you can only invoke a method from a type, so not from an instance of that type. Is there a way to do this anyway?

This line
Type aType = Type.GetType("WatiN.Core" + webElement.Tag)
does not have a dot after Core. It seems as if Core is a namespace and should thus be separated from the Tag name.

The gist of this is you get the Type you want, then use reflection to get the methods, then invoke the given method, passing the instance in.
So, something like this:
Type aType = Type.GetType(string.Format("WatiN.Core.{0}.WatiN.Core", webElement.Tag));
MethodInfo method = aType.GetMethod("TextField");
method.Invoke(this.browser, webElement.Tag, findBy);
The key bits here are:
Get the Type
Get the Method
Invoke the method with the instance you want
There are shortcuts, and I probably don't have the specifics right for your question, but that should get you close to what you want.

Here ya go, threw this together in LINQPad, but should be more-or-less portable:
void Main()
{
WatiN.Core.Browser theBrowser = new WatiN.Core.IE("google.com");
Foo(theBrowser, "Id", "gbqfq", "TextField", "TypeText", "Search for this");
}
public void Foo(WatiN.Core.Browser browser, string findTypeBy, string search, string tagName, string elementMethodName, params object[] argsForMethod)
{
var watinCoreAsm = Assembly.LoadWithPartialName("WatiN.Core");
if(watinCoreAsm == null) return;
var watinCoreTypes = watinCoreAsm.GetTypes();
if(watinCoreTypes == null) return;
var findType = watinCoreTypes.FirstOrDefault(type => type.Name == "Find");
if(findType == null) return;
var constraintInstance = findType.GetMethod("By" + findTypeBy, new Type[]{ typeof(string) }).Invoke(null, new[]{search});
if(constraintInstance == null) return;
var browserAccessor = browser.GetType().GetMethod(tagName, new Type[]{ constraintInstance.GetType() });
if(browserAccessor == null) return;
var resultElement = browserAccessor.Invoke(browser, new[]{ constraintInstance });
if(resultElement == null) return;
var elementMethod = resultElement.GetType().GetMethod(elementMethodName);
if(elementMethod == null) return;
elementMethod.Invoke(resultElement, argsForMethod);
}

Related

How To Use Variable Like a Type?

Here i am trying to create object of WinForm which name is in database table and i get form name by var getChildForm = mnuService.GetChildNodeForm(menuName); service. Since there are other form i have to access as well, so i want to make it generic, but there is an error on line getChildForm usrCrtFrm = new getChildForm(); saying 'getChildForm' is a variable but is used like a type.
Below is my code
var getChildForm = mnuService.GetChildNodeForm(menuName);
getChildForm usrCrtFrm = new getChildForm();
usrCrtFrm.Show();
Below is my service
public string GetChildNodeForm(string menuTitle)
{
var getMenuId = uow.Repository<MainMenu>().FindBy(x => x.MenuTitle == menuTitle).FirstOrDefault().MenuId;
return uow.Repository<MainMenu>().FindBy(x => x.MenuId == getMenuId).FirstOrDefault().Url;
}
Any help will be appreciated .Thank you
If you have the form's Type, it's pretty easy to do what you ask:
var type = typeof(MyForm);
var form = Activator.CreateInstance(type);
But what if you don't have the type? Well, you cannot directly instantiate a form from its title, its "name" (whatever that means), its menu item, or its URL. You will need some mechanism to map one of these back to the form's type.
It's possible some sort of mapping is available; for example, if the have the URL of a page, you might be able to map it to a handler using the ASP.NET framework. But there is no such mechanism that maps titles or menus to WinForm types.
If your database returns the type name you may be able to find the type like this:
public Form InstantiateFormFromTypeName(string typeName)
{
var type = System.Reflection.Assembly.GetExecutingAssembly()
.GetTypes()
.Where
(
t => t.FullName == typeName
&& typeof(Form).IsAssignableFrom(t)
)
.Single();
return Activator.CreateInstance(type) as Form;
}
...but you will need to be careful about scope and name. For example, there may be several types with the same name but in different namespaces or different assemblies.
If no automatic mapping is available, you can create your own, if you know the names of the forms ahead of time. If you store the mappings in a dictionary, for example, you can use it to create instances. Example:
//Define a dictionary where the key is the form name and the value
//is a function that will return a new instance of it.
var formList = new Dictionary<string, Func<Form>>
{
{ "Signon", () => new LoginForm() },
{ "Edit User", () => new EditUserForm() },
{ "Help", () => new FaqForm() }
};
//Use the dictionary to create a form
public Form InstantiateFormBasedOnName(string name)
{
return formList[name](); //Execute the function stored at index "name" in the dictionary
}
getChildForm usrCrtFrm = new getChildForm();
In the above line of code, you are trying to create object of getChildForm which is a var type and you are trying to use a local variable as a type. I found this How to use Local variable as a type? Compiler said “it is a variable but is used like a type”
. Hope it would be useful.
The first statement will return a string which looks like a path (url) of your form.
var getChildForm = mnuService.GetChildNodeForm(menuName);
So this statement return type would be a string which I guess is a form url. It may be something like this.
var childFormUrl = mnuService.GetChildNodeForm(menuName);
After this you should create a class(for example FormCreatorClass) that has a function (for example GetForm, which takes url as parameter)which returns a form object.
Now you can create instance of this class and call the function of the class through the variable of the class instance
var formCreatorInstance = new FormCreatorClass();
var form = fromCreatorInstance.GetForm(childFormUrl);
form.Show();

Xamarin forms duplicate object (deep copy)

I try to use Xamarin Forms (with PCL project). My scope is to make an app to consume public service (exposed via web services).
I try to use TODOASMX Example of Xamarin website. The problem is the code:
static TodoItem FromASMXServiceTodoItem (ASMXService.TodoItem item)
{
return new TodoItem {
ID = item.ID,
Name = item.Name,
Notes = item.Notes,
Done = item.Done
};
}
scope of this code is to copy data from ASMX web Service (ASMXService.TodoItem ) to private domanin (TodoItem). The types are identical, but on namespace different and than the type are different.
In my case the type TodoItem is more, more , more complicated and I need to use a deep copy.
Now I try to use this code for deep copy:
public static object CloneObject(object objSource)
{
//step : 1 Get the type of source object and create a new instance of that type
Type typeSource = objSource.GetType();
object objTarget = Activator.CreateInstance(typeSource);
//Step2 : Get all the properties of source object type
PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//Step : 3 Assign all source property to taget object 's properties
foreach (PropertyInfo property in propertyInfo)
{
//Check whether property can be written to
if (property.CanWrite)
{
//Step : 4 check whether property type is value type, enum or string type
if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(typeof(System.String)))
{
property.SetValue(objTarget, property.GetValue(objSource, null), null);
}
//else property type is object/complex types, so need to recursively call this method until the end of the tree is reached
else
{
object objPropertyValue = property.GetValue(objSource, null);
if (objPropertyValue == null)
{
property.SetValue(objTarget, null, null);
}
else
{
property.SetValue(objTarget, CloneObject(objPropertyValue), null);
}
}
}
}
return objTarget;
}
but when run the code the error is:
System.MissingMethodException: Default constructor not found for type TodoASMX.Droid.MeginetOTA.excInfoByLang[]
Now the type TodoASMX.Droid.MeginetOTA.excInfoByLang[] is not modificable for me and I cannot add default constructor to this type. This type is returned by import of public WebService.
Any workaround (or solution) is appreciated.
Very thanks in advance.
MP
The primary problem is that TodoASMX.Droid.MeginetOTA.excInfoByLang[] is an array. A array has no parameterless constructor, because you need to pass the length of it.
You have to handle arrays separately:
if (typeSource.IsArray)
{
var sourceArray = (Array) objSource;
var length = sourceArray.Length;
var copyArray = (Array)Activator.CreateInstance(typeSource, length);
for (var i = 0; i < length; i++)
{
var value = sourceArray.GetValue(i);
copyArray.SetValue(value, i);
}
}
You approach is maybe a bit complicated. Have a look at https://stackoverflow.com/a/78612/1489968 or search for a already implemented generic clone library.

In C# Webdriver, I want to Click on one link from many which are available on web page

I want to Click on one link from many which are available on web page and I had written one code where I used switch case which is given below. So can any one tell me, can we use enum or any other way to click on particular link. And in return it will give Pageobject.
The code which I am using with switch case with two links,
public Object SelectMenu(string menuName)
{
Object result = null;
IWebElement menuTemp = driver.FindElement(By.XPath(".//a[contains(text(),'" + menuName + "')]"));
if (menuTemp.Enabled)
{
menuTemp.Click();
if (menu.ToUpper() == "COUNTRY")
{
result = new CountryPage(driver).Load();
}
else if (menu.ToUpper() == "PARTNER")
{
result = new PartnerPage(driver).Load();
}
Are you looking something like this ?
string menu = "country";
switch (menu.ToUpper())
{
case "COUNTRY":
result = new CountryPage(driver).Load();
break;
case "PARTNER":
result = new PartnerPage(driver).Load();
break;
}
If you don't want to type out the classes that you expect to instantiate but the naming of the menuitems and your ...Page class is consistent you can lookup your Page types with reflection and bind to the constructor and Load method. An example of such approach is this:
var menu = "Country";
var pageClass = (from asm in AppDomain.CurrentDomain.GetAssemblies()
from pageType in asm.GetTypes()
let ctor = pageType.GetConstructor(new [] {typeof(IWebDriver)})
let load = pageType.GetMethod("Load", Type.EmptyTypes)
where pageType.Name.EndsWith("Page")
&& pageType.Name.StartsWith(menu, StringComparison.InvariantCultureIgnoreCase)
&& ctor != null
&& load != null
select new { Constructor = ctor, Load = load}).Single();
var page = pageClass.Constructor.Invoke(new []{webdriver});
var result = pageClass.Load.Invoke(page, null);
// result has now your CountryPage instance
How does this work:
By calling GetAssemblies all assemblies in the process can be iterated and calling GetTypes returns all public types.
You require that your Page classes accept the IWebDriver in the constructor, so each type is interrogated for that one with the GetConstructor call. A demand is done with GetMethod for the Load requirement.
The last thing to do is checking if the name of the type matches the convention of ending with Page and starting with the menuname.
In the final step a Single (and nothing more) item is returned.
From that result we can create the type by calling invoke and subsequently the invoke method for the Load member.
That final result is returned.
Notice that if you create a type CountryCityPage you'll find two types. I leave it as an exercise for the reader to come up with a solution for that

How can I find previous usages of a variable using Roslyn?

I'm writing a Rosyln analyser/analyzer. It checks to ensure that a method is called before accessing another (potentially dangerous) method on a type. To show what I mean, here's some bad code that I want to analyse and fail on:
private void myMethod()
{
var myThing = new MyThing();
myThing.Value = null;
string value = myThing.GetValue(); // code blows up here as the internal value is null
}
Here's code that's OK because it calls a method that says whether it's null:
private void myMethod()
{
var myThing = new MyThing();
myThing.Value = null;
if(!myThing.HasValue)
{
return ;
}
string value = myThing.GetValue();
}
So, it should check that all calls to GetValue are preceeded by a call to HasValue.
I've just started with Roslyn, so there's probably a more elegant way than my initial (failing) attempt at:
1 - Declare that I want to inspect invocation expressions
context.RegisterSyntaxNodeAction(analyseMemberAccessNode, SyntaxKind.InvocationExpression);
2 - In my method, I get the method name (GetValue())
var expr = (InvocationExpressionSyntax)context.Node;
var memberAccess = expr.Expression as MemberAccessExpressionSyntax;
if (memberAccess?.Name.ToString() != "GetValue")
return;
3 - I then check to see if it's the right 'GetValue'
var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccess).Symbol as IMethodSymbol;
if (!memberSymbol?.OverriddenMethod.ToString().StartsWith("MyNamespace.MyThing.GetValue") ?? true)
return;
4 - Up to here, everything is fine. So I get the name of the variable
var e = memberAccess.Expression as IdentifierNameSyntax;
string variableName = e.Identifier.Text;
5 - now I'm stuck - my theory was to; get the containing method, find the single variable declaration that matches variableName, find usages of that, and ensure that HasValue is called before GetValue.
In short, using a Roslyn analyser (deriving from DiagnosticAnalyzer), how do I ensure that HasValue is called before GetValue?
Instead of registering for each Invocation, you might be better off registering for the entire method declaration. Then you can keep track of all MemberAccessExpressionSyntax and ensure that for a given variable that HasValue is called before GetValue. To do that, I would get the MemberAccessExpressionSyntax descendants from the MethodDeclaration node.
context.RegisterSyntaxNodeAction((analysisContext) =>
{
var invocations =
analysisContext.Node.DescendantNodes().OfType<MemberAccessExpressionSyntax>();
var hasValueCalls = new HashSet<string>();
foreach (var invocation in invocations)
{
var e = invocation.Expression as IdentifierNameSyntax;
if (e == null)
continue;
string variableName = e.Identifier.Text;
if (invocation.Name.ToString() == "HasValue")
{
hasValueCalls.Add(variableName);
}
if (invocation.Name.ToString() == "GetValue")
{
if (!hasValueCalls.Contains(variableName))
{
analysisContext.ReportDiagnostic(Diagnostic.Create(Rule, e.GetLocation()));
}
}
}
}, SyntaxKind.MethodDeclaration);

C# Reflection Lists: Object does not match target type

Trying to add a class object into a List using reflection, but when invoking the Add method with my class object as a parameter, I get 'Object does not match target type'
Here's the snippet code in concern (you can assume classString = "Processor" for now)
PC fetched = new PC();
// Get the appropriate computer field to write to
FieldInfo field = fetched.GetType().GetField(classString);
// Prepare a container by making a new instance of the reffered class
// "CoreView" is the namespace of the program.
object classContainer = Activator.CreateInstance(Type.GetType("CoreView." + classString));
/*
classContainer population code
*/
// This is where I get the error. I know that classContainer is definitely
// the correct type for the list it's being added to at this point.
field.FieldType.GetMethod("Add").Invoke(fetched, new[] {classContainer});
Then this is part of the class the above code is adding classContainers to:
public class PC
{
public List<Processor> Processor = new List<Processor>();
public List<Motherboard> Motherboard = new List<Motherboard>();
// Etc...
}
You're trying to call List.Add(Processor) on PC - you want to call it on the value of the field:
field.FieldType.GetMethod("Add").Invoke(field.GetValue(fetched),
new[] {classContainer});
However, I'd personally advise you not to have public fields like this. Consider using properties instead.
This method will add new item to all list//just instead of insert use Add
IList list = (IList)value;// this what you need to do convert ur parameter value to ilist
if (value == null)
{
return;//or throw an excpetion
}
Type magicType = value.GetType().GetGenericArguments()[0];//Get class type of list
ConstructorInfo magicConstructor = magicType.GetConstructor(Type.EmptyTypes);//Get constructor reference
if (magicConstructor == null)
{
throw new InvalidOperationException(string.Format("Object {0} does not have a default constructor defined", magicType.Name.ToString()));
}
object magicClassObject = magicConstructor.Invoke(new object[] { });//Create new instance
if (magicClassObject == null)
{
throw new ArgumentNullException(string.Format("Class {0} cannot be null.", magicType.Name.ToString()));
}
list.Insert(0, magicClassObject);
list.Add(magicClassObject);

Categories