Delegate as Method Parameter

I know something like this is possible but I’m curious as how to properly implement this.

I want a parameter of a method to accept a method delegate.
I want this method to call the delegate method multiple times.
I want the delegate method to return a boolean value.
I want the delegate method to accept one Object parameter.

I’m mostly not sure how to restrict the delegate type passed and properly invoke it.

What I’m going for is something like this:

dim arrayList2 = arrayList.Where( AddressOf MyCondition)

And have two methods something like this:

[code]Sub Select(extends o2() as object, d as delegate)
dim r() as Object
For i as integer = 0 to o.Ubound
if /* Invoking Delegate on o(i) is true */ then
r.Append(o(i))
end
Next
return r
End Sub

Sub MyCondition(o as Object) as boolean
//some operations on o that returns true or false
End Sub
[/code]

First create your global delegate for this in a module and don’t name it just " delegate"
Than you can use that as parameter.

now in if you can use

if d(o(i)) then

if delegate is declared with the declaration:

MyDelegate(o as Object) as boolean

There’s something not quite right

My Delegate:

Delegate Function DelegateCondition(o as Object) As Boolean

My Where Method

[code]Function Where(extends o() as Object, delegatePtr as ptr) As Object()
dim d as new DelegateCondition(delegatePtr)
dim r() as Object

for i as integer = 0 to o.Ubound
dim b as Boolean
dim o1 as Object = o(i)
if d.invoke(o1) then
r.Append(o1)
end
Next
return r
End Function
[/code]

But when compiling I get the Error on:

 dim d as new DelegateCondition(delegatePtr)

It says External functions cannot use objects as parameters

Thoughts?

leave the line away.
you must declare the function with

Function Where(extends o() as Object, d as DelegateCondition) As Object()

Perfect!

Thank you very much Christian, I’ll see you at the Xojo Conference!

I’m building a library I call Fauxlinq which is based on concepts from linq (C#).

Essentially you can take any array of objects and do stuff like this:

dim myContactsAtLocation() as Contacts allContactsArray.Where("Company.Address.Zip", "=", 58078).Where(AddressOf someConditionMethod).OrderBy(Company.Name).Limit(10).SelectInto(myContactsAtLocation)
Eventually I want to add support for Joins and even sql queries!

Introspection currently follows the “.” notation properly to access property but I want to add support for array elements too. Something like Contacts.Company(5).Address.Zip

By extending Object you’re going to lose the type safety the language provides

You will probably be able to write

dim something as Timer something.Where("Company.Address.Zip", "=", 58078).Where(AddressOf SomeConditionMethod).OrderBy(Company.Name).Limit(10).SelectInto(myContactsAtLocation)
And it will sort of make some measure of “sense” as it will all compile but give you completely bogus results

You’ll get “safety” by convention not by design

The methods only extend an array of objects. So:

dim something as Timer something.Where(...)
Would not work. But:

dim someThings() as Timer someThings.Where(...)
Would work

I think this is desirable and safeguards the convention.

you could overload the method with a lot of possible class names.
maybe even write code generator to create all the combinations.

Instead of extends methods on “object” I’d define an interface then extend the interface
That way it’s less likely to do something unintentional and you get some measure of type safety from the compiler

You add the interface (with no methods) to whatever classes yo want to use this with

[quote=59276:@Christian Schmitz]you could overload the method with a lot of possible class names.
maybe even write code generator to create all the combinations.[/quote]

Its nice that this already works with arrays of any object but it unfortunately doesn’t work with primitives. Instead of creating an overloaded method for every different single primitive, is there a better way.

Currently all I can think of as writing a single extends methods per primitive that converts an array of it into an array of variants.

As far as adding support for things besides Arrays, I know I definitely want SQLQuery implementation but I’m not sure what other classes it would make sense to implement this for besides those two… Maybe Json/XML?

[quote=59308:@Norman Palardy]Instead of extends methods on “object” I’d define an interface then extend the interface
That way it’s less likely to do something unintentional and you get some measure of type safety from the compiler

You add the interface (with no methods) to whatever classes yo want to use this with[/quote]

I guess I’m not sure how the interface would work then. The point of having it Extend on an array of Objects and return an Array of objects is so you can do consecutive “.” functions on it in a single line. This works with any array and doesn’t need to know about the classes ahead of time because it uses introspection. Having to write implementation for every class that has to use this would defeat the purpose. I’m building this so that anywhere in code on any array I can quickly filter the array without needing loops or nesting.

You can extend an interface because an interface IS a type (same as Object) BUT YOU would control what can / cannot be used with your classes a lot better & the compiler could flag errors

And you DONT have to write any implementations - you just want something more type safe than “Object”

The reason

dim someThings() as Timer someThings.Where(…)
Makes virtually no sense in any context so why not get the compiler to help you where it can

Try this

  1. create an interface “LinqObject” - don’t need methods etc JUST need the type
  2. to ANY class you want to participate in this you add the interface
  3. change your code to use LinqObject instead of Object

run
Anything that is NOT a LinqObject will no longer work

[quote=59329:@Norman Palardy]dim someThings() as Timer someThings.Where(…)
Makes virtually no sense in any context so why not get the compiler to help you where it can
[/quote]

Of course this example doesn’t make much sense because someThings is an empty array.
But lets say I have an array of all of my timers.
And I wanted to do something to just my timers that were active

myTimers.Where("Enabled", "=", true).SelectInto(myActiveTimers)

It provides a quick and easy way to filter down an Array.
I use this every day in C# and I wanted something in Xojo to replicate how it works. While it may seem like a silly concept from an outside perspective its darn right convenient. Granted it is much more convenient in C# because C# supports lambda expression

var blarg = Contacts.Where( c => c.Age < 30 && c.Age > 20 && c.IsMale == true).Limit(5)

This works on lists, arrays, dictionaries, IQueryables, IEnumarables etc…
Of course this is an interface in C#. But the core framework supports this, which is something I cannot do hence the extends keyword coming into play. I definitely will include an interface for this, but in the case of dictionaries and arrays I have little choice but to use extends.

If I were to have two additional features in Xojo it would be lambda expressions and the generic type parameter. In fact generic type parameters is something VisualBasic supports as well.
link text

These concepts exist in other languages for a reason. Christian’s mention of “maybe even write code generator to create all the combinations.” would be irrelevant if Generic Types were supported.

I’m wondering if the solution would to just build one extension method that converts an array of objects into a custom linq class, and have that linq class implement the interface.

Something like:

dim myTimers() as timer
dim myActiveTimers() as timer
//fill myTimer array
myTimers.fauxlinq.Where("Enabled", "=", true).SelectInto(myActiveTimers)

That would also allow the Autofill to work once “fauxlinq” it.
I’m thinking this might be a better way. Is this kind of what you meant @Norman Palardy ?

Scratch that, I’m getting an error that “extends modifier cannot be used on a class method” so there’s no point in the interface since “.” notation wont be valid and this:

SelectInto(Limit(OrderBy(Where(myArray, AddressOf myCondition), "Age"), 5), myFilteredArray)

Looks HORRIBLE
compared to:

myArray.Where(AddressOf myCondition).OrderBy("Age").Limit(5).SelectInto(myFilteredArray)

[quote=59350:@Brock Nash]Of course this example doesn’t make much sense because someThings is an empty array.
But lets say I have an array of all of my timers.
And I wanted to do something to just my timers that were active
[/quote]
With extends & object the way its set up you toss out all type safety the compiler might have been able to infer to help you out.
I get WHY you want this but doing it with extends & object means that the ONLY way you can catch anything is to do 100% code coverage testing or just wait til bugs crop up at run time.

I like having the compiler help me where it can at compile time.

But this is your code to live with :slight_smile:

I don’t see this. These extends methods are defined for 1D Object arrays, that’s what they work on and the compiler will say if the type isn’t a 1D Object array. So the typing is strict, it’s just that it’s a very generic type.

Both ways have their pros: with an interface you can apply the feature to a narrow manually selected set, with Object the feature is ubiquitous and more RAD. But given one it’s simply a matter of replacing type to get the other (I think). You could develop a set of method templates having generic type and feed it a list of those types to autogenerate. Probably need two sets of templates, one for Object/iLinq and another for String, Single, Double, UInt8…

The problem with an interface as I discovered above is that you can’t use “extends”. This makes it pretty impractical to use.

Another issue right now is that autofill doesn’t work for these extended methods on object arrays. (Bug?)

I’m thinking that having a new set of templates for each primitive (string, single, double, etc…) is more work than it needs to be. Instead I’m planning on a single method that casts an array of these into an array of variants which counts as an array of objects.

Most certainly can - you can extend ANY type and an interface defines a type.
Enter this into an IDE script & hit run
Since IDE scripting use the same compiler & language you can test this out this way

[code] interface MyLinq
end interface

class myClass
implements myLinq
end class

module myExtensions
sub something( extends m as myLinq)
print “hello world”
end sub
end module

dim c as myClass
c = new myClass
c.something[/code]

[quote=59467:@Norman Palardy]Most certainly can - you can extend ANY type and an interface defines a type.
Enter this into an IDE script & hit run
Since IDE scripting use the same compiler & language you can test this out this way
[/quote]

It didn’t work because I tried including the methods as part of the interface and then had the class try to implement those methods. To me this is how it should be done.

If the interface doesn’t say what methods to implement and the classes that implement that interface can’t have their own implementations of these methods then what is the point of having an interface?

[quote=59747:@Brock Nash]It didn’t work because I tried including the methods as part of the interface and then had the class try to implement those methods. To me this is how it should be done.
[/quote]
Thats what I would do EXCEPT you can’t have an interface on an array which seems to be what you really want.
What’d you define for the methods ?

To define a type that you can extend
Then the extensions can be very specifically targeted at things that implement the interface instead of just “Object”