Is it possible to pass a variable's name along with the value, when passing through functions? - vb.net

I want to know if it's possible to retrieve the variables name from when it was passed into a certain function. For example, if I call parseId(myId) to a function with the signature parseId(id), i can obviously retrieve the value of 'id'. However, is there any way I can retrieve 'myId' as a string (without passing it as another value)?
Specifically in vb.net, but I'm interested in how it would work in any given language.

This is all just random thoughts.. feel free to dismiss or not ;-p
Re your comment about use with stored procedures... if you want to go that route, I wouldn't mess around with the local variable names; that is an implementation detail. However, you could expose those details on an interface method and use the names from there, since that is more formalised - for example (C#):
interface ICustomerRepository {
Customer GetById(int id); // perhaps an attribute to name the sproc
}
You can use similar expression-tree parsing (as discussed here) to get the name and value of the parameter, for example:
var repoWrapper = new Repo<ICustomerRepository>();
int custId = 12345;
var cust = repoWrapper.Execute(r => r.GetById(custId));
Here we'd want to resolve the argument to GetById as "id" (not "custId"), with value 12345. This is actually exactly what my protobuf-net RPC code does ;-p (just don't ask me to translate it to VB - it is hard enough to write it in a language you know well...)

No, you can't do that in the normal sense. What are you really trying to accomplish with this?

You can do this in .NET 3.5 and above using expression trees; I'll knock up a C# example, and try to run it through reflector for VB...
C#:
static void Main()
{
int i = 17;
WriteLine(() => i);
}
static void WriteLine<T>(Expression<Func<T>> expression)
{
string name;
switch (expression.Body.NodeType)
{
case ExpressionType.MemberAccess:
name = ((MemberExpression)expression.Body).Member.Name;
break;
default:
throw new NotSupportedException("Give me a chance!");
}
T val = expression.Compile()();
Console.WriteLine(name + "=" + val);
}
The VB is below, but note that the VB compiler seems to use different names (like $VB$Local_i, not i):
Sub Main()
Dim i As Integer = 17
WriteLine(Function() i)
End Sub
Private Sub WriteLine(Of T)(ByVal expression As Expression(Of Func(Of T)))
If (expression.Body.NodeType <> ExpressionType.MemberAccess) Then
Throw New NotSupportedException("Give me a chance!")
End If
Console.WriteLine((DirectCast(expression.Body, MemberExpression).Member.Name
& "=" & Convert.ToString(expression.Compile.Invoke)))
End Sub

Related

Is it possible to add selector text to a By object

I am wondering if it is possible to add selector text to an existing By object.
That is, suppose I have a popup with a class name ".popup". But then I want to be a little more specific as the webapp is utilized. Now, our popup has an added class name ".cart". My new selector wants to be ".popup.cart". Here is what I have. Is there a better or more efficient way to do this? (I'm coding in C#, but it can be relevant to any language.)
By selector= By.CssSelector("div.popup");
selector = By.CssSelector(
selector.ToString().Remove(0, selector.ToString().LastIndexOf(":") + 1)
+ ".cart");
What is going on above, selector becomes "div.popup".
selector.ToString() returns "By.CssSelector: div.popup"
So I remove all text from the beginning to the last ":" and add the remaining text to ".cart".
selector then becomes "div.popup.cart", which is what I want.
The problem comes when I try to combine any selectors that have been "ByChained." The ToString() method will return By.Chained([By.CssSelector: .popup, By.CssSelector: .title])
If it helps, I am also Sizzle. So, if there's a JQuery way to do this, I'm open.
Put the "div.popup" in a string say "str" like this:-
String str = div.popup
Then you can append ".cart" to the string, make it "div.popup.cart", and use the cssSelector as:
By selector= By.cssSelector(str+".cart");
Note:- Above is a java code.
As mentioned above, I'm basically solving this by unrolling the By.CssSelector().ToString() output and/or the ByChained().ToString() output. There are some caveats. Mainly that the last selector in a ByChained() must be of type By.CssSelector (since I'm adding a JQuery pseudo class):
private static By AddPseudoFunction(By selector, string pseudoFunc)
{
string type = selector.GetType().ToString();
string selectorValue = selector.ToString();
if (type.Contains("Chained"))
{
selectorValue = selectorValue.Remove(0, selector.ToString().IndexOf("[") + 1);
int start = selectorValue.LastIndexOf("]");
int delta = selectorValue.Length - start;
selectorValue = selectorValue.Remove(start, delta);
}
StringBuilder builder = new StringBuilder();
foreach (string s in selectorValue.Split(','))
{
builder.Append(" ");
builder.Append(s.ToString().Remove(0, s.ToString().LastIndexOf(":") + 2));
}
selectorValue = builder.ToString().TrimStart(' ') + pseudoFunc;
return new ByJQuery.ByJQuerySelector(selectorValue, true);
}
Of course, if you want to make this so that it will return a By.CssSelector, just replace the return with:
return By.CssSelector(selctorValue + addSelector);
Where addSelector would be a valid CSS Selector value (like what I describe in the question).

Parsing Expression Tree To Sqlstring - Not Reinventing the wheel

I need to parse an expressiontree to get a sql where clause.
Aren't there any classes in the .NET FX or any third party library which already have this abilities ?
I mean Linq2SQL, EntityFramework , all of them have to do this, so does anyone know, if there is something that can be reused instead of reinventing the wheel?
MyType.Where(Func<TEntity,bool>((entity)=>entity.Id == 5)))
now i need to get the corresponding string representing a where clause:
where abc.Id = "5"
this is just an simple example. it should also work with logical conjunctions.
I know I can create the expressiontree and parse it on my own, but i think there could be something already existing, which I'm missing
You could create an ExpressionVisitor with the sole purpose of finding and processing the where calls. Making this task very easy to handle.
e.g.,
public class WhereCallVisitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
var method = node.Method;
if (method.Name == "Where" && method.DeclaringType == typeof(Queryable))
{ // we are in a Queryable.Where() call
ProcessWhereCall(node);
}
return base.VisitMethodCall(node);
}
private void ProcessWhereCall(MethodCallExpression whereCall)
{
// extract the predicate expression
var predicateExpr = ((dynamic)whereCall.Arguments[1]).Operand as LambdaExpression;
// do stuff with predicateExpr
}
// p.s., dynamic used here to simplify the extraction
}
Then to use it:
var query = from row in table
where row.Foo == "Bar"
select row.Baz;
var visitor = new WhereCallVisitor();
visitor.Visit(query.Expression);

Lambda and VB.NET

I have found this example on StackOverflow:
var people = new List<Person> {
new Person{Name="aaa", Salary=15000, isHip=false}
,new Person{Name="aaa", Salary=15000, isHip=false}
,new Person{Name="bbb", Salary=20000, isHip=false}
,new Person{Name="ccc", Salary=25000, isHip=false}
,new Person{Name="ddd", Salary=30000, isHip=false}
,new Person{Name="eee", Salary=35000, isHip=false}
};
people.Where(p => p.Salary < 25000).Update(p => p.isHip = true);
foreach (var p in people)
{
Console.WriteLine("{0} - {1}", p.Name, p.isHip);
}
public static void Update<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
action(item);
}
In C# everything works fine.
I tried to convert it in VB.NET.
Here's the code:
<System.Runtime.CompilerServices.Extension()> _
Public Sub Update(Of T)(ByVal source As IEnumerable(Of T), ByVal action As Action(Of T))
For Each item In source
action(item)
Next item
End Sub
If I try to update my collection things don't work, though:
people.Where(Function(p) p.Salary < 25000).Update(Function(p) p.isHip = true)
I am using VS2008 (3.5)
This thing is driving me crazy.
Is there anybody who can help me?
Alberto
You should always post what exactly is not working.
In your case, you want to Update list elements, which works though passing an Action(Of T) that should be run for every element.
Such an action, that is just run, performs some side-effects but returns no value is described by exactly one VB construct: A Sub.
Thus what you would want to write is
.Update(Sub(p) p.isHip = true)
which is valid VB2010, but simply does not work in the 2008 version. C# doesn't have a problem there, but in your VB code, you want to pass a Function which has to produce a value and not just perform an assignment. Func(Of ...) would be the appropriate type of that expression.
So what to do?
You can't just express what you want in the syntax of your version. But probably you shouldn't - build a new collection without modifying an old one. Is soon as you're dealing with value types/properties, the above approch won't work at all, since actually a temporary collection returned by Where is modified. Linq is no modification language, but a query system.
Anyway: Just use a plain loop.

Reflection - Iterate object's properties recursively within my own assemblies (Vb.Net/3.5)

I wonder if anyone can help me - I've not done much with reflection but understand the basic principles.
What I'm trying to do:
I'm in the process of developing a class that gathers a lot of information about the local system, network, etc... to be used for automated bug reporting. Instead of having to change my test harness every time I add a new property, I'd (ideally) like to be able to serialise the lot as an XML string and just display that in a textbox.
Unfortunately, the Framework won't use the default XML serializer on read-only properties (which almost all of mine are) as they wouldn't deserialize properly
[Not sure I agree with the assumption that anything serialized must be de-serializable - MS says this is a feature "by design" which I suppose I can understand - Perhaps a tag to indicate that it should be serialized anyway would be advantageous?]
The initial approach was to make properties gettable and settable (with a throw exception on the setter) but the amount of work tidying this up afterwards seems a little excessive and I would want the properties to be read-only in the final version.
What I need help with:
My current plan is to use reflection to recursively iterate through each (public) property of my topmost gathering class. The problem is, the samples I've seen don't handle things recursively. Additionally, I only want to inspect an object's properties if it's in one of my assemblies - Otherwise just call .ToString on it.
If I don't have the inspection limited to my assembly, I assume I'll get (say) a string which then contains a Length which in turn will have .Tostring method...
For the purposes of this project, I can almost guarantee no circular references within my code and as this will only be used as a development tool so I'm not too concerned about it running amok now and then.
I'd appreciate some examples/advice.
Many thanks in advance.
This will hopefully get you started. It prints a tree directly to the console so you'll need to adjust to output XML. Then change the IsMyOwnType method to filter out the assemblies you're interested in, right now it only cares about types in the same assembly as itself.
Shared Sub RecurseProperties(o As Object, level As Integer)
For Each pi As PropertyInfo In o.GetType().GetProperties()
If pi.GetIndexParameters().Length > 0 Then Continue For
Console.Write(New String(" "c, 2 * level))
Console.Write(pi.Name)
Console.Write(" = ")
Dim propValue As Object = pi.GetValue(o, Nothing)
If propValue Is Nothing Then
Console.WriteLine("<null>")
Else
If IsMyOwnType(pi.PropertyType) Then
Console.WriteLine("<object>")
RecurseProperties(propValue, level+1)
Else
Console.WriteLine(propValue.ToString())
End If
End If
Next
End Sub
Shared Function IsMyOwnType(t As Type) As Boolean
Return t.Assembly Is Assembly.GetExecutingAssembly()
End Function
you extension version on c# to use on any object
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Extensions
{
public static class ObjectExtension
{
public static string ToStringProperties(this object o)
{
return o.ToStringProperties(0);
}
public static string ToStringProperties(this object o, int level)
{
StringBuilder sb = new StringBuilder();
string spacer = new String(' ', 2 * level);
if (level == 0) sb.Append(o.ToString());
sb.Append(spacer);
sb.Append("{\r\n");
foreach (PropertyInfo pi in o.GetType().GetProperties())
{
if (pi.GetIndexParameters().Length == 0)
{
sb.Append(spacer);
sb.Append(" ");
sb.Append(pi.Name);
sb.Append(" = ");
object propValue = pi.GetValue(o, null);
if (propValue == null)
{
sb.Append(" <null>");
} else {
if (IsMyOwnType(pi.PropertyType))
{
sb.Append("\r\n");
sb.Append(((object)propValue).ToStringProperties(level + 1));
} else{
sb.Append(propValue.ToString());
}
}
sb.Append("\r\n");
}
}
sb.Append(spacer);
sb.Append("}\r\n");
return sb.ToString();
}
private static bool IsMyOwnType(Type t)
{
return (t.Assembly == Assembly.GetExecutingAssembly());
}
}
}

Iterator pattern in VB.NET (C# would use yield!) [duplicate]

This question already has answers here:
Yield in VB.NET
(8 answers)
Closed 9 years ago.
How do implement the iterator pattern in VB.NET, which does not have the yield keyword?
This is now supported in VS 2010 SP1, with the Async CTP, see: Iterators (C# and Visual Basic) on MSDN and download Visual Studio Async CTP (Version 3).
Code such as this, works:
Private Iterator Function SomeNumbers() As IEnumerable
' Use multiple yield statements.
Yield 3
Yield 5
Yield 8
End Function
VB.NET does not support the creation of custom iterators and thus has no equivalent to the C# yield keyword. However, you might want to look at the KB article How to make a Visual Basic .NET or Visual Basic 2005 class usable in a For Each statement for more information.
C#'s yield keyword forces the compiler to create a state machine in the background to support it. VB.Net does not have the yield keyword. But it does have a construct that would allow you to create a state machine within a function: Static function members.
It should be possible to mimic the effects of a yield return function by creating a generic class that implements IEnumerable as well as the needed state machine and placing an instance as a static member inside your function.
This would, of course, require implementing the class outside of the function. But if done properly the class should be re-usable in the general case. I haven't played with the idea enough to provide any implementation details, though.
Hmm, looks like you might be out of luck:
I was struggling with an issue today when converting some C# to VB.NET. C# has a really cool "yield return" statement that is used in an iterator block to provide a value to the enumerator object. VB.NET does not have the "yield" keyword. So, there are a few solutions (none of which are really clean) to get around this. You could use a return statement to return the value if you are looping through and would like to break an enumerator and return a single value. However, if you'd like to return the entire enumeration, create a List() of the child type and return the list. Since you are usually using this with an IEnumerable, the List() will work nice.
That was written a year ago, not sure if anyone has come up with anything else better since then..
Edit: this will be possible in the version 11 of VB.NET (the one after VS2010), support for iterators is planned. The spec is available here.
Keep in mind that deferred execution and lazy evaluation properties of LINQ expresssions and methods allow us to effectively implement custom iterators until the yield statement is available in .NET 4.5. Yield is used internally by LINQ expressions and methods.
The following code demonstrates this.
Private Sub AddOrRemoveUsersFromRoles(procName As String,
applicationId As Integer,
userNames As String(),
rolenames As String())
Dim sqldb As SqlDatabase = CType(db, SqlDatabase)
Dim command As DbCommand = sqldb.GetStoredProcCommand(procName)
Dim record As New SqlDataRecord({New SqlMetaData("value", SqlDbType.VarChar,200)})
Dim setRecord As Func(Of String, SqlDataRecord) =
Function(value As String)
record.SetString(0, value)
Return record
End Function
Dim userNameRecords As IEnumerable(Of SqlDataRecord) = userNames.Select(setRecord)
Dim roleNameRecords As IEnumerable(Of SqlDataRecord) = rolenames.Select(setRecord)
With sqldb
.AddInParameter(command, "userNames", SqlDbType.Structured, userNameRecords)
.AddInParameter(command, "roleNames", SqlDbType.Structured, roleNameRecords)
.AddInParameter(command, "applicationId", DbType.Int32, applicationId)
.AddInParameter(command, "currentUserName", DbType.String, GetUpdatingUserName)
.ExecuteNonQuery(command)
End With
End Sub
Below gives output: 2, 4, 8, 16, 32
In VB.NET
Public Shared Function setofNumbers() As Integer()
Dim counter As Integer = 0
Dim results As New List(Of Integer)
Dim result As Integer = 1
While counter < 5
result = result * 2
results.Add(result)
counter += 1
End While
Return results.ToArray()
End Function
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
For Each i As Integer In setofNumbers()
MessageBox.Show(i)
Next
End Sub
In C#
private void Form1_Load(object sender, EventArgs e)
{
foreach (int i in setofNumbers())
{
MessageBox.Show(i.ToString());
}
}
public static IEnumerable<int> setofNumbers()
{
int counter=0;
//List<int> results = new List<int>();
int result=1;
while (counter < 5)
{
result = result * 2;
counter += 1;
yield return result;
}
}