I am new to Generic Classes and Generic Methods. I have a simple cpode:
Func<string, string> selector = str => str.ToUpper();
string[] words = { "orange", "apple", "Article", "elephant" };
IEnumerable<String> aWords = words.Select(selector);
When I look at Select method it says:
IEnumerable<String> IEnumerable<String>.Select<String, String>(Func<string,string> collector)
How does Select generic method knows that String,String types are coming? Does it implicitly come from "selector" delegate? I am really confused.
Thanks
The C# compiler infers the type arguments for Select so that you don't have to type them. It burns those types into the compiled assembly.
Does it implicitly come from "selector" delegate?
Exactly. C# has some type inference so that you have to type less.
The Select method itself has no idea what piece of code called it. All it knows is that the type arguments are properly provided.
Related
I'm working on a template literal type that'd only match title cased strings. I thought I'd first create a single word matcher using Uppercase<>:
const uppercaseWord: Uppercase<string> = 'U';
but it seems to match lowercase letters too:
const uppercaseWord: Uppercase<string> = 'u';
this one won't throw.
I've tried parametrizing it:
type SingleUpperCase<Str extends string> = `${Uppercase<Str>}`;
const upperCaseWord: SingleUpperCase = 'U';
But the type argument could not be inferred. It works by passing a string literal explicitly:
type SingleUpperCase<Str extends string> = `${Uppercase<Str>}`;
const upperCaseWord: SingleUpperCase<'u'> = 'U';
it works, but I'd need something more generic. Something that matches any uppercase string. If I tried again passing string as the type argument, the error is resolved, but the lowercase letter can be assigned without an error:
type SingleUpperCase<Str extends string> = `${Uppercase<Str>}`;
const upperCaseWord: SingleUpperCase<string> = 'u'; // no error
I wonder if this is even possible given it'd require a regex-like string matching from the compiler.
Aside: I'm not exactly sure what rules you have for "title case"; I don't know if "HTML String" is in title case or not. For what follows, I will assume that you just need to make sure that the first character of the string and the first character after every space (" ") is not lowercase. That means "HTML String" is fine. If you have a different rule, you can adjust the code in the answer below.
There is no specific type in TypeScript that represents title-cased strings. Template literal types don't give this to you; while Uppercase<string> and Capitalize<string> are now their own types as of TypeScript 4.8, implemented in microsoft/TypeScript#47050, they won't enforce handle the spacing requirements.
For a true title-case string type you would need, as you said, something like regular expression validated string types. There is an open issue at microsoft/TypeScript#41160 asking for use cases for such regex-validated types; if the solution below doesn't meet your needs, you might want to comment on that issue with your use case, why it is compelling, and why the alternative solutions don't suffice.
While there is no specific type that works here, you can write a recursive template literal type TitleCase<T> which can be used as a constraint on T. Meaning that T extends TitleCase<T> if and only if T is a title-cased string.
Then, in order to save people from having to annotate their strings with some generic type, you'd write a helper function like asTitleCase() which just returns its input, but produces a compiler error if you pass in a bad string.
So, while your ideal solution here would look like this:
/* THIS IS NOT POSSIBLE
const okay: TitleCase = "This Is Fine"; // okay
const error: TitleCase = "This is not fine"; // error
const alsoError: TitleCase = String(Math.random()); // error
*/
the implementable solution looks like this:
const okay = asTitleCase("This Is Fine"); // no error
const error = asTitleCase("This is not fine"); // error!
// ---------------------> ~~~~~~~~~~~~~~~~~~
// Argument of type '"This is not fine"' is not assignable to
// parameter of type '"This Is Not Fine"'.
const alsoError = asTitleCase(String(Math.random())); // error!
// Argument of type 'string' is not assignable to parameter of type
// '"Please Use a Title Cased String Literal Here, Thx"'
Again, this is what is implementable, not what is ideal. All uses of title-cased string types will need to gain an extra generic type parameter.
Note that you probably don't need to actually write asTitleCase(...) unless you want to see the error right at the declaration. Presumably you have some function (say, lookupBookTitle()) that cares about title case. If so, you'd just make that function generic and enforce the constraint there. So instead of const str = asTitleCase("XXX"); lookupBookTitle(str);, you'd just write const str = "XXX"; lookupBookTitle(str); The only difference is where the error shows up.
Also, inside the implementation of something like lookupBookTitle(), you should probably just widen the input to string and just treat it as if it's already been validated. Even though T extends TitleCase<T> has the effect of enforcing the constraint on callers, the compiler won't be able to follow the logic when T is an unspecified generic type parameter:
// callers see a function that constrains title to TitleCase
function lookupBookTitle<T extends string>(title: VerifyTitleCase<T>): Book;
// implementer just uses string
function lookupBookTitle(title: string) {
const book = db.lookupByTitle(title);
if (!book) throw new Error("NO BOOK");
return book;
}
Anyway, here's the implementation:
type TitleCase<T extends string, D extends string = " "> =
string extends T ? never :
T extends `${infer F}${D}${infer R}` ?
`${Capitalize<F>}${D}${TitleCase<R, D>}` : Capitalize<T>;
The type TitleCase<T, D> splits the string T at the delimiter D, and capitalizes (first character is uppercased) each piece. So it turns a string into a title-cased version of itself:
type X = TitleCase<"the quick brown fox jumps over the lazy dog.">
// type X = "The Quick Brown Fox Jumps Over The Lazy Dog."
Then we can write a VerifyTitleCase<T> type that checks if T extends TitleCase<T>. If so, it resolves to T. If not, it resolves either to TitleCase<T>, or some hard-coded error string that hopefully gives users an idea what went wrong. (There are no "throw types" or "Invalid types" in TypeScript, as requested in microsoft/TypeScript#23689; so using a hard-coded error string literal is a workaround):
type VerifyTitleCase<T extends string> = T extends TitleCase<T> ? T :
TitleCase<T> extends never ? "Please Use a Title Cased String Literal Here, Thx" :
TitleCase<T>
And finally, the helper function:
const asTitleCase = <T extends string>(s: VerifyTitleCase<T>) => s;
Playground link to code
void main() {
final list = [1].cast<int>();
print('${list.runtimeType}'); // prints CastList<int, int>
}
When I type lis..., Android Studio code completion infers the type as List<int> which it is but when I use runtimeType on it, it prints CastList<int, int>.
Is this IDE fault or Dart?
The cast method is specified to return a List:
List<R> cast <R>()
So this is what your IDE are based on since this is the only guarantee your get. But since other classes can extend/implement List, we are also allowed to return a class which extends from List and return objects based on this new type of List.
E.g. in you example we can see that cast actually returns an instance of CastList. But since CastList implements the interface of List the cast method are allowed to return the CastList instance.
And this is one of the reason why you should never really use runtimeType for anything other than debugging. runtimeType will return the specific type of the object without any details about implemented interfaces.
Instead, you should use the is operator if you want to test for a given type like:
if (obj is List<int>)
The is operator will test if obj implements the interface of e.g. List<int> and not just test if obj is created from this specific class.
Groovy supports both default, and named arguments. I just dont see them working together.
I need some classes to support construction using simple non named arguments, and using named arguments like below:
def a1 = new A(2)
def a2 = new A(a: 200, b: "non default")
class A extends SomeBase {
def props
A(a=1, b="str") {
_init(a, b)
}
A(args) {
// use the values in the args map:
_init(args.a, args.b)
props = args
}
private _init(a, b) {
}
}
Is it generally good practice to support both at the same time? Is the above code the only way to it?
The given code will cause some problems. In particular, it'll generate two constructors with a single Object parameter. The first constructor generates bytecode equivalent to:
A() // a,b both default
A(Object) // a set, b default
A(Object, Object) // pass in both
The second generates this:
A(Object) // accepts any object
You can get around this problem by adding some types. Even though groovy has dynamic typing, the type declarations in methods and constructors still matter. For example:
A(int a = 1, String b = "str") { ... }
A(Map args) { ... }
As for good practices, I'd simply use one of the groovy.transform.Canonical or groovy.transform.TupleConstructor annotations. They will provide correct property map and positional parameter constructors automatically. TupleConstructor provides the constructors only, Canonical applies some other best practices with regards to equals, hashCode, and toString.
Say I have a function
def method1(MyClass2 mc2) {...}
and I call it with an object of type MyClass1. Is there a way that I can specify how to implicitly convert from MyClass1 to MyClass2, so that the method call will work without having to explicitly say method1(mc1 as MyClass2)?
If MyClass1 doesn't implement/extend MyClass2, there isn't anything that I'm aware of that'll do the "as MyClass2" conversion without the old standby Java method overloading. Explicitly overloading the method with the signature including MyClass1:
def method1(MyClass1 mc1) {
method1(mc1 as MyClass2)
}
The other, more groovy, alternative is to not explicitly type method1 so that it doesn't demand that you have an instance of MyClass2:
def method1(mc) {
// do stuff and let mc walk/talk/quack like MyClass2
// or even do the "as MyClass2" in this method if you need it for something further down.
}
So, I'm trying to figure out Expression trees. I'm trying to add in a dynamic equals to a Queryable where T is one of several different tables. I'm first checking the table contains the field I want to filter on.
ParameterExpression param = Expression.Parameter(typeof(TSource), "x");
Expression conversionExpression = Expression.Convert(Expression.Property(param, _sourceProperty), typeof(TList));
Expression<Func<TSource, TList>> propertyExpression = Expression.Lambda<Func<TSource, TList>>(conversionExpression, param);
Expression<Func<TList, TList, bool>> methodExpression = (x, y) => x.Equals(y);
ReadOnlyCollection<ParameterExpression> parameters = propertyExpression.Parameters;
InvocationExpression getFieldPropertyExpression = Expression.Invoke(
propertyExpression,
parameters.Cast<Expression>());
MethodCallExpression methodBody = methodExpression.Body as MethodCallExpression;
MethodCallExpression methodCall = Expression.Call(methodBody.Method, Expression.Constant(equalTo), getFieldPropertyExpression);
Expression<Func<TSource, bool>> equalsStatement = Expression.Lambda<Func<TSource, bool>>(methodCall, parameters);
return source.Where(equalsStatement);
When I execute this, I get an issue with the MethodInfo in the Call statement. It tells me;
Static method requires null instance, non-static method requires non-null instance.
I'm no master of Expression trees, but I think I understand about 75% of what I'm doing here and know what I'm trying to achieve. The TList is a bad name right now, but I took this from an example that works to produce an In statement just fine.
I'm really looking for an explanation here so I can work through the code myself, or a solution with an explanation of what I was missing.
Edit:
Ok, so after a very frustrating afternoon and still not quite feeling like I understand what I'm looking at entirely, I think I have an answer.
ParameterExpression sourceObject = Expression.Parameter(typeof(TSource), "x");
Expression<Func<TSource, bool>> check = Expression.Lambda<Func<TSource, bool>>
(
Expression.Equal(
Expression.MakeMemberAccess(sourceObject, typeof(TSource).GetProperty(_sourceProperty)),
Expression.Constant(equalTo)
),
sourceObject
);
return source.Where(check);
Is anybody able to explain to me why the original just wasn't fit for what I was trying to do? I want to understand more about the actual process, but I feel I'm not picking it up as fast as I would like.
Expression.Call has two sets of overloads (with lots of overloads in each). One set is for instance methods and the other set is for static methods. In those for static methods, the first argument is a MethodInfo object -- exactly like you have. For instance methods, the first argument should be an Expression representing the target (i.e. the left-hand-side of the "." in a method call.) Given the error you are receiving, it sounds like the MethodInfo represents a non-static method, and therefore you must provide an expression representing the instance as the first argument.