对象的比较与排序(七):对泛型列表进行排序和搜索:Comparison<T>和Predicate<T>的应用...

本文介绍如何利用泛型委托Comparison<T>和Predicate<T>实现列表的排序和搜索功能,通过定义相应的比较方法和搜索条件,使得对列表进行高效的操作成为可能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面说到,我们可以使用 IComparer<T> 和 IComparable<T> 接口比较两个对象,然后对该类型的对象列表排序。现在我们来介绍另两个委托可以来用排序和搜索列表

先总结一下前面的内容:

MSDN上List有如下的sort重载:

如果调用无参的sort()方法,那么要求集合中的元素要实现 System.IComparable 接口,否则此方法会抛出InvalidOperationException异常。

如果集合的元素没有实现IComparable接口,则可以调用Sort(IComparer<T>),这时我们要创建一个类实现IComparer接口作为比较器来完成排序。

或者更为简单些,不需要定义一个比较器,直接给sort方法提供一个用于"比较两对象”大小的方法即可---实现Comparison<T>委托。

可以参见另一日记:

http://www.cnblogs.com/eagle1986/archive/2012/02/07/2341719.html

 

一般情况下,给列表排序需要有一个方法来比较T类型的两个对象。要在列表中搜索,也需要一个方法来检查T类型的对象,看看它是否满足某个条件。定义这样的方法很简单,这里给出两个可以使用的泛型委托。

1. Comparision<T>  这个委托类型用于排序方法,其返回类型和参数是 int method(T object A , T objectB)

2.Predicate<T> 这个委托类型用于搜索方法,其返回类型和参数是 bool method(T targetObject).

可以定义任意个这样的方法,使用它们实现 List<T> 的搜索和排序方法。

 

 

定义一个Vector类

Vector类
public class Vector
{
public double? R = null;
public double? Theta = null;

public double? ThetaRadians
{
get
{
// Convert degrees to radians.
return (Theta * Math.PI / 180.0);
}
}

public Vector(double? r, double? theta)
{
// Normalize.
if (r < 0)
{
r = -r;
theta += 180;
}
theta = theta % 360;

// Assign fields.
R = r;
Theta = theta;
}

public static Vector operator +(Vector op1, Vector op2)
{
try
{
// Get (x, y) coordinates for new vector.
double newX = op1.R.Value * Math.Sin(op1.ThetaRadians.Value)
+ op2.R.Value * Math.Sin(op2.ThetaRadians.Value);
double newY = op1.R.Value * Math.Cos(op1.ThetaRadians.Value)
+ op2.R.Value * Math.Cos(op2.ThetaRadians.Value);

// Convert to (r, theta).
double newR = Math.Sqrt(newX * newX + newY * newY);
double newTheta = Math.Atan2(newX, newY) * 180.0 / Math.PI;

// Return result.
return new Vector(newR, newTheta);
}
catch
{
// Return "null" vector.
return new Vector(null, null);
}
}

public static Vector operator -(Vector op1)
{
return new Vector(-op1.R, op1.Theta);
}

public static Vector operator -(Vector op1, Vector op2)
{
return op1 + (-op2);
}

public override string ToString()
{
// Get string representation of coordinates.
string rString = R.HasValue ? R.ToString() : "null";
string thetaString = Theta.HasValue ? Theta.ToString() : "null";

// Return (r, theta) string.
return string.Format("({0}, {1})", rString, thetaString);
}
}

 

下面定义一个集合类 Vectors

集合类
    public class Vectors : List<Vector>
{
public Vectors()
{
}

public Vectors(IEnumerable<Vector> initialItems)
{
foreach (Vector vector in initialItems)
{
Add(vector);
}
}

public string Sum()
{
StringBuilder sb = new StringBuilder();
Vector currentPoint = new Vector(0.0, 0.0);
sb.Append("origin");
foreach (Vector vector in this)
{
sb.AppendFormat(" + {0}", vector);
currentPoint += vector;
}
sb.AppendFormat(" = {0}", currentPoint);
return sb.ToString();
}
}

 

 

 

下面就是我们要定义的用于排序和搜索的方法。

View Code
    public static class VectorDelegates
{
public static int Compare(Vector x, Vector y)
{
if (x.R > y.R)
{
return 1;
}
else if (x.R < y.R)
{
return -1;
}
return 0;
}

public static bool TopRightQuadrant(Vector target)
{
if (target.Theta >= 0.0 && target.Theta <= 90.0)
{
return true;
}
else
{
return false;
}
}
}


上面的代码,  Compare用于比较,TopRightQuadrant用于搜索。

 

下面是测试代码。

测试代码
 class Program
{
static void Main(string[] args)
{
Vectors route = new Vectors();
route.Add(new Vector(2.0, 90.0));
route.Add(new Vector(1.0, 180.0));
route.Add(new Vector(0.5, 45.0));
route.Add(new Vector(2.5, 315.0));

Console.WriteLine(route.Sum());

Comparison<Vector> sorter = new Comparison<Vector>(VectorDelegates.Compare);
route.Sort(sorter);
Console.WriteLine(route.Sum());

Predicate<Vector> searcher =
new Predicate<Vector>(VectorDelegates.TopRightQuadrant);
Vectors topRightQuadrantRoute = new Vectors(route.FindAll(searcher));
Console.WriteLine(topRightQuadrantRoute.Sum());

Console.ReadKey();
}
}



总结:

1、关于 Predicate<T> 更详细用法,参见http://www.cnblogs.com/eagle1986/archive/2012/01/19/2327351.html

2、可以参见上一个日记,可以用Lamda表达式来使代码更简洁。

 

import _ = require("../index"); declare module "../index" { interface LoDashStatic { /** * Attempts to invoke func, returning either the result or the caught error object. Any additional arguments * are provided to func when it’s invoked. * * @param func The function to attempt. * @return Returns the func result or error object. */ attempt<TResult>(func: (...args: any[]) => TResult, ...args: any[]): TResult | Error; } interface LoDashImplicitWrapper<TValue> { /** * @see _.attempt */ attempt<TResult>(...args: any[]): TResult | Error; } interface LoDashExplicitWrapper<TValue> { /** * @see _.attempt */ attempt<TResult>(...args: any[]): ExpChain<TResult | Error>; } interface LoDashStatic { /** * Binds methods of an object to the object itself, overwriting the existing method. Method names may be * specified as individual arguments or as arrays of method names. If no method names are provided all * enumerable function properties, own and inherited, of object are bound. * * Note: This method does not set the "length" property of bound functions. * * @param object The object to bind and assign the bound methods to. * @param methodNames The object method names to bind, specified as individual method names or arrays of * method names. * @return Returns object. */ bindAll<T>(object: T, ...methodNames: Array<Many<string>>): T; } interface LoDashImplicitWrapper<TValue> { /** * @see _.bindAll */ bindAll(...methodNames: Array<Many<string>>): this; } interface LoDashExplicitWrapper<TValue> { /** * @see _.bindAll */ bindAll(...methodNames: Array<Many<string>>): this; } interface LoDashStatic { /** * Creates a function that iterates over `pairs` and invokes the corresponding * function of the first predicate to return truthy. The predicate-function * pairs are invoked with the `this` binding and arguments of the created * function. * * @since 4.0.0 * @category Util * @param pairs The predicate-function pairs. * @returns Returns the new composite function. * @example * * var func = _.cond([ * [_.matches({ 'a': 1 }), _.constant('matches A')], * [_.conforms({ 'b': _.isNumber }), _.constant('matches B')], * [_.stubTrue, _.constant('no match')] * ]); * * func({ 'a': 1, 'b': 2 }); * // => 'matches A' * * func({ 'a': 0, 'b': 1 }); * // => 'matches B' * * func({ 'a': '1', 'b': '2' }); * // => 'no match' */ cond<T, R>(pairs: Array<CondPair<T, R>>): (Target: T) => R; } type ConformsPredicateObject<T> = { [P in keyof T]: T[P] extends (arg: infer A) => any ? A : any }; interface LoDashStatic { /** * Creates a function that invokes the predicate properties of `source` with the corresponding * property values of a given object, returning true if all predicates return truthy, else false. */ conforms<T>(source: ConformsPredicateObject<T>): (value: T) => boolean; } interface LoDashImplicitWrapper<TValue> { /** * @see _.conforms */ conforms(): Function<(value: ConformsPredicateObject<TValue>) => boolean>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.conforms */ conforms(): FunctionChain<(value: ConformsPredicateObject<TValue>) => boolean>; } interface LoDashStatic { /** * Creates a function that returns value. * * @param value The value to return from the new function. * @return Returns the new function. */ constant<T>(value: T): () => T; } interface LoDashImplicitWrapper<TValue> { /** * @see _.constant */ constant(): Function<() => TValue>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.constant */ constant(): FunctionChain<() => TValue>; } interface LoDashStatic { /** * Checks `value` to determine whether a default value should be returned in * its place. The `defaultValue` is returned if `value` is `NaN`, `null`, * or `undefined`. * * @param value The value to check. * @param defaultValue The default value. * @returns Returns the resolved value. */ defaultTo<T>(value: T | null | undefined, defaultValue: T): T; /** * @see _.defaultTo */ defaultTo<T, TDefault>(value: T | null | undefined, defaultValue: TDefault): T | TDefault; } interface LoDashImplicitWrapper<TValue> { /** * @see _.defaultTo */ defaultTo(defaultValue: TValue): TValue; /** * @see _.defaultTo */ defaultTo<TDefault>(defaultValue: TDefault): TValue extends null | undefined ? TDefault : TValue | TDefault; } interface LoDashExplicitWrapper<TValue> { /** * @see _.defaultTo */ defaultTo(defaultValue: TValue): ExpChain<TValue>; /** * @see _.defaultTo */ defaultTo<TDefault>(defaultValue: TDefault): ExpChain<TValue extends null | undefined ? TDefault : TValue | TDefault>; } interface LoDashStatic { /** * Creates a function that returns the result of invoking the provided functions with the this binding of the * created function, where each successive invocation is supplied the return value of the previous. * * @param funcs Functions to invoke. * @return Returns the new function. */ flow<A extends any[], R1, R2, R3, R4, R5, R6, R7>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (...args: A) => R7; /** * @see _.flow */ flow<A extends any[], R1, R2, R3, R4, R5, R6, R7>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7, ...func: Array<Many<(a: any) => any>>): (...args: A) => any; /** * @see _.flow */ flow<A extends any[], R1, R2, R3, R4, R5, R6>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (...args: A) => R6; /** * @see _.flow */ flow<A extends any[], R1, R2, R3, R4, R5>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (...args: A) => R5; /** * @see _.flow */ flow<A extends any[], R1, R2, R3, R4>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (...args: A) => R4; /** * @see _.flow */ flow<A extends any[], R1, R2, R3>(f1: (...args: A) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (...args: A) => R3; /** * @see _.flow */ flow<A extends any[], R1, R2>(f1: (...args: A) => R1, f2: (a: R1) => R2): (...args: A) => R2; /** * @see _.flow */ flow(...func: Array<Many<(...args: any[]) => any>>): (...args: any[]) => any; } interface Function<T extends (...arg: any) => any> { /** * @see _.flow */ flow<R2, R3, R4, R5, R6, R7>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): Function<(...args: Parameters<T>) => R7>; /** * @see _.flow */ flow<R2, R3, R4, R5, R6, R7>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7, ...func: Array<Many<(a: any) => any>>): Function<(...args: Parameters<T>) => any>; /** * @see _.flow */ flow<R2, R3, R4, R5, R6>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): Function<(...args: Parameters<T>) => R6>; /** * @see _.flow */ flow<R2, R3, R4, R5>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): Function<(...args: Parameters<T>) => R5>; /** * @see _.flow */ flow<R2, R3, R4>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): Function<(...args: Parameters<T>) => R4>; /** * @see _.flow */ flow<R2, R3>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3): Function<(...args: Parameters<T>) => R3>; /** * @see _.flow */ flow<R2>(f2: (a: ReturnType<T>) => R2): Function<(...args: Parameters<T>) => R2>; /** * @see _.flow */ flow(...func: Array<Many<(...args: any[]) => any>>): Function<(...args: any[]) => any>; } interface FunctionChain<T> { /** * @see _.flow */ flow<R2, R3, R4, R5, R6, R7>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): FunctionChain<(...args: Parameters<T>) => R7>; /** * @see _.flow */ flow<R2, R3, R4, R5, R6, R7>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7, ...func: Array<Many<(a: any) => any>>): FunctionChain<(...args: Parameters<T>) => any>; /** * @see _.flow */ flow<R2, R3, R4, R5, R6>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): FunctionChain<(...args: Parameters<T>) => R6>; /** * @see _.flow */ flow<R2, R3, R4, R5>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): FunctionChain<(...args: Parameters<T>) => R5>; /** * @see _.flow */ flow<R2, R3, R4>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): FunctionChain<(...args: Parameters<T>) => R4>; /** * @see _.flow */ flow<R2, R3>(f2: (a: ReturnType<T>) => R2, f3: (a: R2) => R3): FunctionChain<(...args: Parameters<T>) => R3>; /** * @see _.flow */ flow<R2>(f2: (a: ReturnType<T>) => R2): FunctionChain<(...args: Parameters<T>) => R2>; /** * @see _.flow */ flow(...func: Array<Many<(...args: any[]) => any>>): FunctionChain<(...args: any[]) => any>; } interface LoDashStatic { /** * This method is like _.flow except that it creates a function that invokes the provided functions from right * to left. * * @param funcs Functions to invoke. * @return Returns the new function. */ flowRight<A extends any[], R1, R2, R3, R4, R5, R6, R7>(f7: (a: R6) => R7, f6: (a: R5) => R6, f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R7; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4, R5, R6>(f6: (a: R5) => R6, f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R6; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4, R5>(f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R5; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4>(f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R4; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3>(f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R3; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2>(f2: (a: R1) => R2, f1: (...args: A) => R1): (...args: A) => R2; /** * @see _.flowRight */ flowRight(...func: Array<Many<(...args: any[]) => any>>): (...args: any[]) => any; } interface Function<T> { /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4, R5>(f6: (a: R5) => Parameters<T>["0"], f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4>(f5: (a: R4) => Parameters<T>["0"], f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3>(f4: (a: R3) => Parameters<T>["0"], f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2>(f3: (a: R2) => Parameters<T>["0"], f2: (a: R1) => R2, f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1>(f2: (a: R1) => Parameters<T>["0"], f1: (...args: A) => R1): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[]>(f1: (...args: A) => Parameters<T>["0"]): Function<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight(...func: Array<Many<(...args: any[]) => any>>): Function<(...args: any[]) => any>; } interface FunctionChain<T> { /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4, R5>(f6: (a: R5) => Parameters<T>["0"], f5: (a: R4) => R5, f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3, R4>(f5: (a: R4) => Parameters<T>["0"], f4: (a: R3) => R4, f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2, R3>(f4: (a: R3) => Parameters<T>["0"], f3: (a: R2) => R3, f2: (a: R1) => R2, f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1, R2>(f3: (a: R2) => Parameters<T>["0"], f2: (a: R1) => R2, f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[], R1>(f2: (a: R1) => Parameters<T>["0"], f1: (...args: A) => R1): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight<A extends any[]>(f1: (...args: A) => Parameters<T>["0"]): FunctionChain<(...args: A) => ReturnType<T>>; /** * @see _.flowRight */ flowRight(...func: Array<Many<(...args: any[]) => any>>): FunctionChain<(...args: any[]) => any>; } interface LoDashStatic { /** * This method returns the first argument provided to it. * * @param value Any value. * @return Returns value. */ identity<T>(value: T): T; /** * @see _.identity */ identity(): undefined; } interface LoDashImplicitWrapper<TValue> { /** * @see _.identity */ identity(): TValue; } interface LoDashExplicitWrapper<TValue> { /** * @see _.identity */ identity(): this; } interface LoDashStatic { /** * Creates a function that invokes `func` with the arguments of the created * function. If `func` is a property name the created callback returns the * property value for a given element. If `func` is an object the created * callback returns `true` for elements that contain the equivalent object properties, otherwise it returns `false`. * * @category Util * @param [func=_.identity] The value to convert to a callback. * @returns Returns the callback. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 } * ]; * * // create custom iteratee shorthands * _.iteratee = _.wrap(_.iteratee, function(callback, func) { * var p = /^(\S+)\s*([<>])\s*(\S+)$/.exec(func); * return !p ? callback(func) : function(object) { * return (p[2] == '>' ? object[p[1]] > p[3] : object[p[1]] < p[3]); * }; * }); * * _.filter(users, 'age > 36'); * // => [{ 'user': 'fred', 'age': 40 }] */ iteratee<TFunction extends (...args: any[]) => any>(func: TFunction): TFunction; /** * @see _.iteratee */ iteratee(func: string | object): (...args: any[]) => any; } interface Function<T extends (...args: any) => any> { /** * @see _.iteratee */ iteratee(): Function<T>; } interface Collection<T> { /** * @see _.iteratee */ iteratee(): Function<(o: object) => boolean>; } interface Object<T> { /** * @see _.iteratee */ iteratee(): Function<(o: T) => boolean>; } interface String { /** * @see _.iteratee */ iteratee(): Function<(o: object) => any>; } interface FunctionChain<T extends (...args: any) => any> { /** * @see _.iteratee */ iteratee(): FunctionChain<T>; } interface CollectionChain<T> { /** * @see _.iteratee */ iteratee(): FunctionChain<(o: object) => boolean>; } interface ObjectChain<T> { /** * @see _.iteratee */ iteratee(): FunctionChain<(o: T) => boolean>; } interface StringChain { /** * @see _.iteratee */ iteratee(): FunctionChain<(o: object) => any>; } interface StringNullableChain { /** * @see _.iteratee */ iteratee(): FunctionChain<(o: object) => any>; } interface LoDashStatic { /** * Creates a function that performs a deep comparison between a given object and source, returning true if the * given object has equivalent property values, else false. * * Note: This method supports comparing arrays, booleans, Date objects, numbers, Object objects, regexes, and * strings. Objects are compared by their own, not inherited, enumerable properties. For comparing a single own * or inherited property value see _.matchesProperty. * * @param source The object of property values to match. * @return Returns the new function. */ matches<T>(source: T): (value: any) => boolean; /** * @see _.matches */ matches<T, V>(source: T): (value: V) => boolean; } interface LoDashImplicitWrapper<TValue> { /** * @see _.matches */ matches<V>(): Function<(value: V) => boolean>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.matches */ matches<V>(): FunctionChain<(value: V) => boolean>; } interface LoDashStatic { /** * Creates a function that compares the property value of path on a given object to value. * * Note: This method supports comparing arrays, booleans, Date objects, numbers, Object objects, regexes, and * strings. Objects are compared by their own, not inherited, enumerable properties. * * @param path The path of the property to get. * @param srcValue The value to match. * @return Returns the new function. */ matchesProperty<T>(path: PropertyPath, srcValue: T): (value: any) => boolean; /** * @see _.matchesProperty */ matchesProperty<T, V>(path: PropertyPath, srcValue: T): (value: V) => boolean; } interface LoDashImplicitWrapper<TValue> { /** * @see _.matchesProperty */ matchesProperty<SrcValue>(srcValue: SrcValue): Function<(value: any) => boolean>; /** * @see _.matchesProperty */ matchesProperty<SrcValue, Value>(srcValue: SrcValue): Function<(value: Value) => boolean>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.matchesProperty */ matchesProperty<SrcValue>(srcValue: SrcValue): FunctionChain<(value: any) => boolean>; /** * @see _.matchesProperty */ matchesProperty<SrcValue, Value>(srcValue: SrcValue): FunctionChain<(value: Value) => boolean>; } interface LoDashStatic { /** * Creates a function that invokes the method at path on a given object. Any additional arguments are provided * to the invoked method. * * @param path The path of the method to invoke. * @param args The arguments to invoke the method with. * @return Returns the new function. */ method(path: PropertyPath, ...args: any[]): (object: any) => any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.method */ method(...args: any[]): Function<(object: any) => any>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.method */ method(...args: any[]): FunctionChain<(object: any) => any>; } interface LoDashStatic { /** * The opposite of _.method; this method creates a function that invokes the method at a given path on object. * Any additional arguments are provided to the invoked method. * * @param object The object to query. * @param args The arguments to invoke the method with. * @return Returns the new function. */ methodOf(object: object, ...args: any[]): (path: PropertyPath) => any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.methodOf */ methodOf(...args: any[]): LoDashImplicitWrapper<(path: PropertyPath) => any>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.methodOf */ methodOf(...args: any[]): LoDashExplicitWrapper<(path: PropertyPath) => any>; } interface MixinOptions { /** * @see _.chain */ chain?: boolean; } interface LoDashStatic { /** * Adds all own enumerable function properties of a source object to the destination object. If object is a * function then methods are added to its prototype as well. * * Note: Use _.runInContext to create a pristine lodash function to avoid conflicts caused by modifying * the original. * * @param object The destination object. * @param source The object of functions to add. * @param options The options object. * @param options.chain Specify whether the functions added are chainable. * @return Returns object. */ mixin<TObject>(object: TObject, source: Dictionary<(...args: any[]) => any>, options?: MixinOptions): TObject; /** * @see _.mixin */ mixin<TResult>(source: Dictionary<(...args: any[]) => any>, options?: MixinOptions): LoDashStatic; } interface LoDashImplicitWrapper<TValue> { /** * @see _.mixin */ mixin(source: Dictionary<(...args: any[]) => any>, options?: MixinOptions): this; /** * @see _.mixin */ mixin(options?: MixinOptions): LoDashImplicitWrapper<LoDashStatic>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.mixin */ mixin(source: Dictionary<(...args: any[]) => any>, options?: MixinOptions): this; /** * @see _.mixin */ mixin(options?: MixinOptions): LoDashExplicitWrapper<LoDashStatic>; } interface LoDashStatic { /** * Reverts the _ variable to its previous value and returns a reference to the lodash function. * * @return Returns the lodash function. */ noConflict(): typeof _; } interface LoDashImplicitWrapper<TValue> { /** * @see _.noConflict */ noConflict(): typeof _; } interface LoDashExplicitWrapper<TValue> { /** * @see _.noConflict */ noConflict(): LoDashExplicitWrapper<typeof _>; } interface LoDashStatic { /** * A no-operation function that returns undefined regardless of the arguments it receives. * * @return undefined */ noop(...args: any[]): void; } interface LoDashImplicitWrapper<TValue> { /** * @see _.noop */ noop(...args: any[]): void; } interface LoDashExplicitWrapper<TValue> { /** * @see _.noop */ noop(...args: any[]): PrimitiveChain<undefined>; } interface LoDashStatic { /** * Creates a function that returns its nth argument. * * @param n The index of the argument to return. * @return Returns the new function. */ nthArg(n?: number): (...args: any[]) => any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.nthArg */ nthArg(): Function<(...args: any[]) => any>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.nthArg */ nthArg(): FunctionChain<(...args: any[]) => any>; } interface LoDashStatic { /** * Creates a function that invokes iteratees with the arguments provided to the created function and returns * their results. * * @param iteratees The iteratees to invoke. * @return Returns the new function. */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): (...args: any[]) => TResult[]; } interface Collection<T> { /** * @see _.over */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): Function<(...args: any[]) => TResult[]>; } interface Function<T> { /** * @see _.over */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): Function<(...args: any[]) => Array<ReturnType<T> | TResult>>; } interface CollectionChain<T> { /** * @see _.over */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): FunctionChain<(...args: any[]) => TResult[]>; } interface FunctionChain<T> { /** * @see _.over */ over<TResult>(...iteratees: Array<Many<(...args: any[]) => TResult>>): FunctionChain<(...args: any[]) => Array<ReturnType<T> | TResult>>; } interface LoDashStatic { /** * Creates a function that checks if all of the predicates return truthy when invoked with the arguments * provided to the created function. * * @param predicates The predicates to check. * @return Returns the new function. */ overEvery<T, Result1 extends T, Result2 extends T>(...predicates: [ (arg: T) => arg is Result1, (arg: T) => arg is Result2 ]): (arg: T) => arg is Result1 & Result2; overEvery<T>(...predicates: Array<Many<(...args: T[]) => boolean>>): (...args: T[]) => boolean; } interface Collection<T> { /** * @see _.overEvery */ overEvery<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): Function<(...args: TArgs[]) => boolean>; } interface Function<T> { /** * @see _.overEvery */ overEvery<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): Function<(...args: Parameters<T> | TArgs[]) => boolean>; } interface CollectionChain<T> { /** * @see _.overEvery */ overEvery<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): FunctionChain<(...args: TArgs[]) => boolean>; } interface FunctionChain<T> { /** * @see _.overEvery */ overEvery<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): FunctionChain<(...args: Parameters<T> | TArgs[]) => boolean>; } interface LoDashStatic { /** * Creates a function that checks if any of the predicates return truthy when invoked with the arguments * provided to the created function. * * @param predicates The predicates to check. * @return Returns the new function. */ overSome<T, Result1 extends T, Result2 extends T>(...predicates: [ (arg: T) => arg is Result1, (arg: T) => arg is Result2 ]): (arg: T) => arg is Result1 | Result2; overSome<T>(...predicates: Array<Many<(...args: T[]) => boolean>>): (...args: T[]) => boolean; } interface Collection<T> { /** * @see _.overSome */ overSome<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): Function<(...args: TArgs[]) => boolean>; } interface Function<T> { /** * @see _.overSome */ overSome<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): Function<(...args: Parameters<T> | TArgs[]) => boolean>; } interface CollectionChain<T> { /** * @see _.overSome */ overSome<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): FunctionChain<(...args: TArgs[]) => boolean>; } interface FunctionChain<T> { /** * @see _.overSome */ overSome<TArgs>(...iteratees: Array<Many<(...args: TArgs[]) => boolean>>): FunctionChain<(...args: Parameters<T> | TArgs[]) => boolean>; } interface LoDashStatic { /** * Creates a function that returns the property value at path on a given object. * * @param path The path of the property to get. * @return Returns the new function. */ property<TObj, TResult>(path: PropertyPath): (obj: TObj) => TResult; } interface LoDashImplicitWrapper<TValue> { /** * @see _.property */ property<TObj, TResult>(): Function<(obj: TObj) => TResult>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.property */ property<TObj, TResult>(): FunctionChain<(obj: TObj) => TResult>; } interface LoDashStatic { /** * The opposite of _.property; this method creates a function that returns the property value at a given path * on object. * * @param object The object to query. * @return Returns the new function. */ propertyOf<T extends {}>(object: T): (path: PropertyPath) => any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.propertyOf */ propertyOf(): LoDashImplicitWrapper<(path: PropertyPath) => any>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.propertyOf */ propertyOf(): LoDashExplicitWrapper<(path: PropertyPath) => any>; } interface LoDashStatic { /** * Creates an array of numbers (positive and/or negative) progressing from start up to, but not including, end. * If end is not specified it’s set to start with start then set to 0. If end is less than start a zero-length * range is created unless a negative step is specified. * * @param start The start of the range. * @param end The end of the range. * @param step The value to increment or decrement by. * @return Returns a new range array. */ range(start: number, end?: number, step?: number): number[]; /** * @see _.range */ range(end: number, index: string | number, guard: object): number[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.range */ range(end?: number, step?: number): Collection<number>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.range */ range(end?: number, step?: number): CollectionChain<number>; } interface LoDashStatic { /** * This method is like `_.range` except that it populates values in * descending order. * * @category Util * @param start The start of the range. * @param end The end of the range. * @param step The value to increment or decrement by. * @returns Returns the new array of numbers. * @example * * _.rangeRight(4); * // => [3, 2, 1, 0] * * _.rangeRight(-4); * // => [-3, -2, -1, 0] * * _.rangeRight(1, 5); * // => [4, 3, 2, 1] * * _.rangeRight(0, 20, 5); * // => [15, 10, 5, 0] * * _.rangeRight(0, -4, -1); * // => [-3, -2, -1, 0] * * _.rangeRight(1, 4, 0); * // => [1, 1, 1] * * _.rangeRight(0); * // => [] */ rangeRight(start: number, end?: number, step?: number): number[]; /** * @see _.rangeRight */ rangeRight(end: number, index: string | number, guard: object): number[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.rangeRight */ rangeRight(end?: number, step?: number): Collection<number>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.rangeRight */ rangeRight(end?: number, step?: number): CollectionChain<number>; } interface LoDashStatic { /** * Create a new pristine lodash function using the given context object. * * @param context The context object. * @return Returns a new lodash function. */ runInContext(context?: object): LoDashStatic; } interface LoDashImplicitWrapper<TValue> { /** * @see _.runInContext */ runInContext(): LoDashStatic; } interface LoDashStatic { /** * This method returns a new empty array. * * @returns Returns the new empty array. */ stubArray(): any[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubArray */ stubArray(): any[]; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubArray */ stubArray(): CollectionChain<any>; } interface LoDashStatic { /** * This method returns `false`. * * @returns Returns `false`. */ stubFalse(): false; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubFalse */ stubFalse(): false; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubFalse */ stubFalse(): PrimitiveChain<false>; } interface LoDashStatic { /** * This method returns a new empty object. * * @returns Returns the new empty object. */ stubObject(): any; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubObject */ stubObject(): any; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubObject */ stubObject(): LoDashExplicitWrapper<any>; } interface LoDashStatic { /** * This method returns an empty string. * * @returns Returns the empty string. */ stubString(): string; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubString */ stubString(): string; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubString */ stubString(): StringChain; } interface LoDashStatic { /** * This method returns `true`. * * @returns Returns `true`. */ stubTrue(): true; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubTrue */ stubTrue(): true; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubTrue */ stubTrue(): PrimitiveChain<true>; } interface LoDashStatic { /** * Invokes the iteratee function n times, returning an array of the results of each invocation. The iteratee * is invoked with one argument; (index). * * @param n The number of times to invoke iteratee. * @param iteratee The function invoked per iteration. * @return Returns the array of results. */ times<TResult>(n: number, iteratee: (num: number) => TResult): TResult[]; /** * @see _.times */ times(n: number): number[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.times */ times<TResult>(iteratee: (num: number) => TResult): TResult[]; /** * @see _.times */ times(): number[]; } interface LoDashExplicitWrapper<TValue> { /** * @see _.times */ times<TResult>(iteratee: (num: number) => TResult): CollectionChain<TResult>; /** * @see _.times */ times(): CollectionChain<number>; } interface LoDashStatic { /** * Converts `value` to a property path array. * * @category Util * @param value The value to convert. * @returns Returns the new property path array. * @example * * _.toPath('a.b.c'); * // => ['a', 'b', 'c'] * * _.toPath('a[0].b.c'); * // => ['a', '0', 'b', 'c'] * * var path = ['a', 'b', 'c'], * newPath = _.toPath(path); * * console.log(newPath); * // => ['a', 'b', 'c'] * * console.log(path === newPath); * // => false */ toPath(value: any): string[]; } interface LoDashImplicitWrapper<TValue> { /** * @see _.toPath */ toPath(): Collection<string>; } interface LoDashExplicitWrapper<TValue> { /** * @see _.toPath */ toPath(): CollectionChain<string>; } interface LoDashStatic { /** * Generates a unique ID. If prefix is provided the ID is appended to it. * * @param prefix The value to prefix the ID with. * @return Returns the unique ID. */ uniqueId(prefix?: string): string; } interface LoDashImplicitWrapper<TValue> { /** * @see _.uniqueId */ uniqueId(): string; } interface LoDashExplicitWrapper<TValue> { /** * @see _.uniqueId */ uniqueId(): StringChain; } // stubTrue interface LoDashStatic { /** * This method returns true. * * @return Returns true. */ stubTrue(): true; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubTrue */ stubTrue(): true; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubTrue */ stubTrue(): LoDashExplicitWrapper<true>; } // stubFalse interface LoDashStatic { /** * This method returns false. * * @return Returns false. */ stubFalse(): false; } interface LoDashImplicitWrapper<TValue> { /** * @see _.stubFalse */ stubFalse(): false; } interface LoDashExplicitWrapper<TValue> { /** * @see _.stubFalse */ stubFalse(): LoDashExplicitWrapper<false>; } } 报A rest parameter must be of an array type.错误
07-31
<think>我们被要求解释Java中类参数`<T>`通配符`<?>`的区别。根据提供的引用材料,我们可以总结如下: 引用[1]提到允许在编写类、接口方法时使用参数化类,使得代码更加灵活、通用安全。 引用[2][3]详细介绍了通配符`<?>`, `<? extends T>`, `<? super T>`,并指出了它们的使用场景区别。 引用[4]也讨论了这三种通配符的使用场景。 现在,我们重点区分`<T>``<?>`: 1. **定义位置不同**: - `<T>`:是类参数(Type Parameter),用于定义类、接口或方法时声明类变量。例如: ```java public class Box<T> { ... } // 类 public <T> void method(T t) { ... } // 方法 ``` - `<?>`:是通配符(Wildcard),用于使用时表示未知类。它不能用于定义类或方法,只能用于变量声明、方法参数等。例如: ```java List<?> list = ...; // 可以接受任何类的List ``` 2. **使用目的不同**: - `<T>`:用于在编写代码时声明一个类变量,这个类变量可以在整个类或方法中使用,代表一个具体的类(尽管在定义时未知,但在使用时会被指定)。 - `<?>`:用于表示一个未知的类,通常用于提高API的灵活性,允许接受多种类。 3. **类约束**: - `<T>`:在定义类或方法时,我们可以对`T`进行约束(如`<T extends Number>`),这样`T`就被限定为Number或其子类。 - `<?>`:通配符本身也可以有上界或下界,如`<? extends Number>`或`<? super Integer>`,但无界通配符`<?>`表示未知类,没有任何约束(除了Object)。 4. **类引用**: - 使用`<T>`时,我们可以用`T`作为类来声明变量、参数、返回类等。例如: ```java public T get() { ... } // 在类中 public <T> T method(T t) { return t; } // 在方法中 ``` - 使用`<?>`时,我们不能直接引用该类(因为不知道具体类),只能使用Object类的方法(因为任何类都是Object的子类)。例如: ```java List<?> list = ...; Object o = list.get(0); // 正确,因为任何对象都是Object // 但你不能添加元素(除了null),因为类未知 list.add(null); // 允许,因为null是所有引用类的值 list.add("hello"); // 错误,因为不知道list的具体类 ``` 5. **应用场景**: - `<T>`:当你需要在整个类或方法中多次引用同一个类,或者需要执行类相关的操作(如创建数组、类转换等)时使用。 - `<?>`:当你需要编写一个可以操作多种类容器的方法,且不关心容器中元素的具体类时使用。例如,一个打印List中所有元素的方法: ```java public void printList(List<?> list) { for (Object o : list) { System.out.println(o); } } ``` 6. **类安全**: - 使用`<T>`可以保证类安全,因为类在编译时就被确定,并且在整个类或方法中保持一致。 - 使用`<?>`同样保证类安全,因为它限制了我们对容器的操作(例如,不能添加元素,只能读取为Object),从而避免类错误。 **总结**: | 特性 | 类参数 `<T>` | 通配符 `<?>` | |--------------|----------------------------------------|----------------------------------------| | 定义位置 | 类/接口/方法的定义 | 变量声明、方法参数等使用的地方 | | 是否可命名 | 是,`T`是一个类变量名 | 否,`?`表示未知类 | | 类约束 | 可通过`extends`/`super`约束 | 可通过`extends`/`super`约束(如`? extends Number`) | | 类引用 | 可以用`T`作为类使用 | 只能作为未知类,读取为Object | | 添加元素 | 可以添加`T`类对象 | 只能添加`null`(因为类未知) | | 适用场景 | 需要多次引用同一类 | 只需要读取,不需要知道具体类 | **示例代码**: ```java // 使用类参数T的方法 public static <T> void fillList(List<T> list, T item) { for (int i = 0; i < list.size(); i++) { list.set(i, item); } } // 使用通配符?的方法(只能读取) public static void printList(List<?> list) { for (Object o : list) { System.out.println(o); } } ``` 在`fillList`方法中,我们使用`<T>`,因为我们需要在方法中多次使用同一个类`T`(作为参数类列表元素类)。而在`printList`方法中,我们只需要读取元素,所以使用`<?>`即可。 [^1]: 变量,通配符 <?> ,原理及应用详解。 [^2]: 深度解析 Java 通配符 `<? super T>` `<? extends T>`。 [^3]: Java中的通配符<?><? extends T>、<? super T>的区别。 [^4]: 中的通配符<?>、<? extends T>、<? super T>的使用场景。 §§相关问题§§ 1. Java中`<? extends T>``<? super T>`分别适用于什么场景? 2. 擦除对Java有什么影响? 3. 在定义类时,如何正确使用类边界(extends/super)?</think>在 Java 中,类参数 `<T>` 通配符 `<?>` 是两种不同的概念,主要区别体现在**定义位置**、**使用场景****类约束**上: --- ### 1. **定义位置作用** | **类** | **定义位置** | **作用** | |-------------------|-------------------------------|--------------------------------------------------------------------------| | **`<T>`** | 类/方法**定义**时声明 | 表示一个**具体的类参数**,在代码中可被引用(如 `T` 可作为变量类)[^1] | | **`<?>`** | 类/方法**使用**时声明 | 表示**未知类**(通配符),仅用于接受任意类,无法直接引用[^3] | **示例**: ```java // 定义时使用 <T>(类参数) class Box<T> { private T value; // T 可被引用 } // 使用时使用 <?>(通配符) void printList(List<?> list) { // ? 不可被直接引用,只能通过 Object 操作 for (Object obj : list) System.out.println(obj); } ``` --- ### 2. **类约束能力** | **类** | **类约束** | |-------------------|----------------------------------------------------------------------------| | **`<T>`** | 可添加边界:`<T extends Number>`(限定 `T` 必须是 `Number` 的子类)[^1] | | **`<?>`** | 无界通配符,表示完全未知的类(等同于 `<? extends Object>`)[^3] | **示例**: ```java // <T> 可约束类 <T extends Number> T sum(List<T> list) { ... } // <?> 无约束,但无法添加元素 void process(List<?> list) { list.add(new Object()); // 编译错误!无法确定具体类 } ``` --- ### 3. **使用场景** | **场景** | **适用类** | **原因** | |------------------------|--------------|--------------------------------------------------------------------------| | **定义类/方法** | `<T>` | 需在代码中引用具体类(如字段、返回值)[^1] | | **读取集合内容** | `<?>` | 只需读取元素(协变),不关心具体类[^3] | | **写入集合** | `<T>` 或 `<? super T>` | 需明确写入类(`<?>` 无法写入)[^4] | | **实现类安全API** | `<T>` | 需在编译期检查类一致性(如 `Collections.sort()`)[^2] | **示例**: ```java // 场景1:定义方法(使用 <T>) public static <T> void addToList(List<T> list, T item) { list.add(item); // 安全,类一致 } // 场景2:读取任意集合(使用 <?>) public static void printAll(List<?> list) { for (Object obj : list) System.out.println(obj); // 安全读取 } // 场景3:写入集合(使用 <? super T>) public static <T> void addNumbers(List<? super T> dest, T item) { dest.add(item); // 安全,dest 是 T 的父类 } ``` --- ### 4. **核心区别总结** | **特性** | **`<T>`** | **`<?>`** | |----------------------|--------------------------------------|----------------------------------------| | **可引用性** | ✅ 可作为类使用(如 `T value`) | ❌ 只能通过 `Object` 操作 | | **写入能力** | ✅ 可添加 `T` 类元素 | ❌ 只能添加 `null` | | **读取能力** | ✅ 返回 `T` 类 | ✅ 返回 `Object` 类 | | **类边界控制** | ✅ 支持 `extends`/`super` | ✅ 支持 `extends`/`super`(如 `<? extends Number>`) | | **适用阶段** | 定义阶段 | 使用阶段 | > 💡 **简单记忆**: > - 需要**操作具体类**时用 `<T>`(如定义类、方法或写入数据)。 > - 只需**读取数据且不关心类**时用 `<?>`。 [^1]: 变量,通配符 <?> ,原理及应用详解。 [^2]: 深度解析 Java 通配符 `<? super T>` `<? extends T>`。 [^3]: Java中的通配符<?><? extends T>、<? super T>的区别。 [^4]: 中的通配符<?>、<? extends T>、<? super T>的使用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值